mirror of
https://github.com/ziglang/zig.git
synced 2025-12-31 02:23:22 +00:00
* add safety panic for resuming a function which is returning, pending an await * remove IrInstructionResultPtr * add IrInstructionReturnBegin. This does the early return in async functions; does nothing in normal functions. * `await` gets a result location * `analyze_fn_async` will call `analyze_fn_body` if necessary. * async function frames have a result pointer field for themselves to access and one for the awaiter to supply before the atomic rmw. when returning, async functions copy the result to the awaiter result pointer, if it is non-null. * async function frames have a stack trace pointer which is supplied by the awaiter before the atomicrmw. Later in the frame is a stack trace struct and addresses, which is used for its own calls and awaits. * when awaiting an async function, if an early return occurred, the awaiter tail resumes the frame. * when an async function returns, early return does a suspend (in IrInstructionReturnBegin) before copying the error return trace data, result, and running the defers. After the last defer runs, the frame will no longer be accessed. * proper acquire/release atomic ordering attributes in async functions.
25107 lines
1.1 MiB
25107 lines
1.1 MiB
/*
|
|
* Copyright (c) 2016 Andrew Kelley
|
|
*
|
|
* This file is part of zig, which is MIT licensed.
|
|
* See http://opensource.org/licenses/MIT
|
|
*/
|
|
|
|
#include "analyze.hpp"
|
|
#include "ast_render.hpp"
|
|
#include "error.hpp"
|
|
#include "ir.hpp"
|
|
#include "ir_print.hpp"
|
|
#include "os.hpp"
|
|
#include "range_set.hpp"
|
|
#include "softfloat.hpp"
|
|
#include "translate_c.hpp"
|
|
#include "util.hpp"
|
|
|
|
#include <errno.h>
|
|
|
|
struct IrExecContext {
|
|
ZigList<ConstExprValue *> mem_slot_list;
|
|
};
|
|
|
|
struct IrBuilder {
|
|
CodeGen *codegen;
|
|
IrExecutable *exec;
|
|
IrBasicBlock *current_basic_block;
|
|
};
|
|
|
|
struct IrAnalyze {
|
|
CodeGen *codegen;
|
|
IrBuilder old_irb;
|
|
IrBuilder new_irb;
|
|
IrExecContext exec_context;
|
|
size_t old_bb_index;
|
|
size_t instruction_index;
|
|
ZigType *explicit_return_type;
|
|
AstNode *explicit_return_type_source_node;
|
|
ZigList<IrInstruction *> src_implicit_return_type_list;
|
|
ZigList<IrSuspendPosition> resume_stack;
|
|
IrBasicBlock *const_predecessor_bb;
|
|
};
|
|
|
|
enum ConstCastResultId {
|
|
ConstCastResultIdOk,
|
|
ConstCastResultIdInvalid,
|
|
ConstCastResultIdErrSet,
|
|
ConstCastResultIdErrSetGlobal,
|
|
ConstCastResultIdPointerChild,
|
|
ConstCastResultIdSliceChild,
|
|
ConstCastResultIdOptionalChild,
|
|
ConstCastResultIdErrorUnionPayload,
|
|
ConstCastResultIdErrorUnionErrorSet,
|
|
ConstCastResultIdFnAlign,
|
|
ConstCastResultIdFnCC,
|
|
ConstCastResultIdFnVarArgs,
|
|
ConstCastResultIdFnIsGeneric,
|
|
ConstCastResultIdFnReturnType,
|
|
ConstCastResultIdFnArgCount,
|
|
ConstCastResultIdFnGenericArgCount,
|
|
ConstCastResultIdFnArg,
|
|
ConstCastResultIdFnArgNoAlias,
|
|
ConstCastResultIdType,
|
|
ConstCastResultIdUnresolvedInferredErrSet,
|
|
ConstCastResultIdAsyncAllocatorType,
|
|
ConstCastResultIdBadAllowsZero,
|
|
};
|
|
|
|
struct ConstCastOnly;
|
|
struct ConstCastArg {
|
|
size_t arg_index;
|
|
ZigType *actual_param_type;
|
|
ZigType *expected_param_type;
|
|
ConstCastOnly *child;
|
|
};
|
|
|
|
struct ConstCastArgNoAlias {
|
|
size_t arg_index;
|
|
};
|
|
|
|
struct ConstCastOptionalMismatch;
|
|
struct ConstCastPointerMismatch;
|
|
struct ConstCastSliceMismatch;
|
|
struct ConstCastErrUnionErrSetMismatch;
|
|
struct ConstCastErrUnionPayloadMismatch;
|
|
struct ConstCastErrSetMismatch;
|
|
struct ConstCastTypeMismatch;
|
|
struct ConstCastBadAllowsZero;
|
|
|
|
struct ConstCastOnly {
|
|
ConstCastResultId id;
|
|
union {
|
|
ConstCastErrSetMismatch *error_set_mismatch;
|
|
ConstCastPointerMismatch *pointer_mismatch;
|
|
ConstCastSliceMismatch *slice_mismatch;
|
|
ConstCastOptionalMismatch *optional;
|
|
ConstCastErrUnionPayloadMismatch *error_union_payload;
|
|
ConstCastErrUnionErrSetMismatch *error_union_error_set;
|
|
ConstCastTypeMismatch *type_mismatch;
|
|
ConstCastOnly *return_type;
|
|
ConstCastOnly *null_wrap_ptr_child;
|
|
ConstCastArg fn_arg;
|
|
ConstCastArgNoAlias arg_no_alias;
|
|
ConstCastBadAllowsZero *bad_allows_zero;
|
|
} data;
|
|
};
|
|
|
|
struct ConstCastTypeMismatch {
|
|
ZigType *wanted_type;
|
|
ZigType *actual_type;
|
|
};
|
|
|
|
struct ConstCastOptionalMismatch {
|
|
ConstCastOnly child;
|
|
ZigType *wanted_child;
|
|
ZigType *actual_child;
|
|
};
|
|
|
|
struct ConstCastPointerMismatch {
|
|
ConstCastOnly child;
|
|
ZigType *wanted_child;
|
|
ZigType *actual_child;
|
|
};
|
|
|
|
struct ConstCastSliceMismatch {
|
|
ConstCastOnly child;
|
|
ZigType *wanted_child;
|
|
ZigType *actual_child;
|
|
};
|
|
|
|
struct ConstCastErrUnionErrSetMismatch {
|
|
ConstCastOnly child;
|
|
ZigType *wanted_err_set;
|
|
ZigType *actual_err_set;
|
|
};
|
|
|
|
struct ConstCastErrUnionPayloadMismatch {
|
|
ConstCastOnly child;
|
|
ZigType *wanted_payload;
|
|
ZigType *actual_payload;
|
|
};
|
|
|
|
struct ConstCastErrSetMismatch {
|
|
ZigList<ErrorTableEntry *> missing_errors;
|
|
};
|
|
|
|
struct ConstCastBadAllowsZero {
|
|
ZigType *wanted_type;
|
|
ZigType *actual_type;
|
|
};
|
|
|
|
|
|
enum UndefAllowed {
|
|
UndefOk,
|
|
UndefBad,
|
|
};
|
|
|
|
static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope);
|
|
static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval,
|
|
ResultLoc *result_loc);
|
|
static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, ZigType *expected_type);
|
|
static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *ptr,
|
|
ResultLoc *result_loc);
|
|
static ErrorMsg *exec_add_error_node(CodeGen *codegen, IrExecutable *exec, AstNode *source_node, Buf *msg);
|
|
static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name,
|
|
IrInstruction *source_instr, IrInstruction *container_ptr, ZigType *container_type, bool initializing);
|
|
static void ir_assert(bool ok, IrInstruction *source_instruction);
|
|
static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, ZigVar *var);
|
|
static ZigType *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op);
|
|
static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval, ResultLoc *result_loc);
|
|
static IrInstruction *ir_expr_wrap(IrBuilder *irb, Scope *scope, IrInstruction *inst, ResultLoc *result_loc);
|
|
static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_align);
|
|
static ZigType *adjust_slice_align(CodeGen *g, ZigType *slice_type, uint32_t new_align);
|
|
static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf, ConstExprValue *val);
|
|
static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue *val);
|
|
static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node,
|
|
ConstExprValue *out_val, ConstExprValue *ptr_val);
|
|
static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *ptr,
|
|
ZigType *dest_type, IrInstruction *dest_type_src, bool safety_check_on);
|
|
static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, UndefAllowed undef_allowed);
|
|
static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_global_refs);
|
|
static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align);
|
|
static IrInstruction *ir_analyze_int_to_ptr(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target,
|
|
ZigType *ptr_type);
|
|
static IrInstruction *ir_analyze_bit_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value,
|
|
ZigType *dest_type);
|
|
static IrInstruction *ir_resolve_result_raw(IrAnalyze *ira, IrInstruction *suspend_source_instr,
|
|
ResultLoc *result_loc, ZigType *value_type, IrInstruction *value, bool force_runtime, bool non_null_comptime);
|
|
static IrInstruction *ir_resolve_result(IrAnalyze *ira, IrInstruction *suspend_source_instr,
|
|
ResultLoc *result_loc, ZigType *value_type, IrInstruction *value, bool force_runtime,
|
|
bool non_null_comptime, bool allow_discard);
|
|
static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstruction *source_instr,
|
|
IrInstruction *base_ptr, bool safety_check_on, bool initializing);
|
|
static IrInstruction *ir_analyze_unwrap_error_payload(IrAnalyze *ira, IrInstruction *source_instr,
|
|
IrInstruction *base_ptr, bool safety_check_on, bool initializing);
|
|
static IrInstruction *ir_analyze_unwrap_err_code(IrAnalyze *ira, IrInstruction *source_instr,
|
|
IrInstruction *base_ptr, bool initializing);
|
|
static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr,
|
|
IrInstruction *ptr, IrInstruction *uncasted_value, bool allow_write_through_const);
|
|
static IrInstruction *ir_gen_union_init_expr(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *union_type, IrInstruction *field_name, AstNode *expr_node,
|
|
LVal lval, ResultLoc *parent_result_loc);
|
|
|
|
static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *const_val) {
|
|
assert(get_src_ptr_type(const_val->type) != nullptr);
|
|
assert(const_val->special == ConstValSpecialStatic);
|
|
ConstExprValue *result;
|
|
|
|
switch (type_has_one_possible_value(g, const_val->type->data.pointer.child_type)) {
|
|
case OnePossibleValueInvalid:
|
|
zig_unreachable();
|
|
case OnePossibleValueYes:
|
|
result = create_const_vals(1);
|
|
result->type = const_val->type->data.pointer.child_type;
|
|
result->special = ConstValSpecialStatic;
|
|
return result;
|
|
case OnePossibleValueNo:
|
|
break;
|
|
}
|
|
|
|
switch (const_val->data.x_ptr.special) {
|
|
case ConstPtrSpecialInvalid:
|
|
zig_unreachable();
|
|
case ConstPtrSpecialRef:
|
|
result = const_val->data.x_ptr.data.ref.pointee;
|
|
break;
|
|
case ConstPtrSpecialBaseArray: {
|
|
ConstExprValue *array_val = const_val->data.x_ptr.data.base_array.array_val;
|
|
expand_undef_array(g, array_val);
|
|
result = &array_val->data.x_array.data.s_none.elements[const_val->data.x_ptr.data.base_array.elem_index];
|
|
break;
|
|
}
|
|
case ConstPtrSpecialBaseStruct: {
|
|
ConstExprValue *struct_val = const_val->data.x_ptr.data.base_struct.struct_val;
|
|
result = &struct_val->data.x_struct.fields[const_val->data.x_ptr.data.base_struct.field_index];
|
|
break;
|
|
}
|
|
case ConstPtrSpecialBaseErrorUnionCode:
|
|
result = const_val->data.x_ptr.data.base_err_union_code.err_union_val->data.x_err_union.error_set;
|
|
break;
|
|
case ConstPtrSpecialBaseErrorUnionPayload:
|
|
result = const_val->data.x_ptr.data.base_err_union_payload.err_union_val->data.x_err_union.payload;
|
|
break;
|
|
case ConstPtrSpecialBaseOptionalPayload:
|
|
result = const_val->data.x_ptr.data.base_optional_payload.optional_val->data.x_optional;
|
|
break;
|
|
case ConstPtrSpecialNull:
|
|
result = const_val;
|
|
break;
|
|
case ConstPtrSpecialHardCodedAddr:
|
|
zig_unreachable();
|
|
case ConstPtrSpecialDiscard:
|
|
zig_unreachable();
|
|
case ConstPtrSpecialFunction:
|
|
zig_unreachable();
|
|
}
|
|
assert(result != nullptr);
|
|
return result;
|
|
}
|
|
|
|
static bool is_opt_err_set(ZigType *ty) {
|
|
return ty->id == ZigTypeIdErrorSet ||
|
|
(ty->id == ZigTypeIdOptional && ty->data.maybe.child_type->id == ZigTypeIdErrorSet);
|
|
}
|
|
|
|
static bool is_slice(ZigType *type) {
|
|
return type->id == ZigTypeIdStruct && type->data.structure.is_slice;
|
|
}
|
|
|
|
static bool slice_is_const(ZigType *type) {
|
|
assert(is_slice(type));
|
|
return type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const;
|
|
}
|
|
|
|
// This function returns true when you can change the type of a ConstExprValue and the
|
|
// value remains meaningful.
|
|
static bool types_have_same_zig_comptime_repr(ZigType *a, ZigType *b) {
|
|
if (a == b)
|
|
return true;
|
|
|
|
if (get_codegen_ptr_type(a) != nullptr && get_codegen_ptr_type(b) != nullptr)
|
|
return true;
|
|
|
|
if (is_opt_err_set(a) && is_opt_err_set(b))
|
|
return true;
|
|
|
|
if (a->id != b->id)
|
|
return false;
|
|
|
|
switch (a->id) {
|
|
case ZigTypeIdInvalid:
|
|
case ZigTypeIdUnreachable:
|
|
zig_unreachable();
|
|
case ZigTypeIdMetaType:
|
|
case ZigTypeIdVoid:
|
|
case ZigTypeIdBool:
|
|
case ZigTypeIdComptimeFloat:
|
|
case ZigTypeIdComptimeInt:
|
|
case ZigTypeIdEnumLiteral:
|
|
case ZigTypeIdPointer:
|
|
case ZigTypeIdUndefined:
|
|
case ZigTypeIdNull:
|
|
case ZigTypeIdBoundFn:
|
|
case ZigTypeIdErrorSet:
|
|
case ZigTypeIdOpaque:
|
|
case ZigTypeIdAnyFrame:
|
|
return true;
|
|
case ZigTypeIdFloat:
|
|
return a->data.floating.bit_count == b->data.floating.bit_count;
|
|
case ZigTypeIdInt:
|
|
return a->data.integral.is_signed == b->data.integral.is_signed;
|
|
case ZigTypeIdStruct:
|
|
return is_slice(a) && is_slice(b);
|
|
case ZigTypeIdArray:
|
|
case ZigTypeIdOptional:
|
|
case ZigTypeIdErrorUnion:
|
|
case ZigTypeIdEnum:
|
|
case ZigTypeIdUnion:
|
|
case ZigTypeIdFn:
|
|
case ZigTypeIdArgTuple:
|
|
case ZigTypeIdVector:
|
|
case ZigTypeIdCoroFrame:
|
|
return false;
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
static bool ir_should_inline(IrExecutable *exec, Scope *scope) {
|
|
if (exec->is_inline)
|
|
return true;
|
|
|
|
while (scope != nullptr) {
|
|
if (scope->id == ScopeIdCompTime)
|
|
return true;
|
|
if (scope->id == ScopeIdFnDef)
|
|
break;
|
|
scope = scope->parent;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void ir_instruction_append(IrBasicBlock *basic_block, IrInstruction *instruction) {
|
|
assert(basic_block);
|
|
assert(instruction);
|
|
basic_block->instruction_list.append(instruction);
|
|
}
|
|
|
|
static size_t exec_next_debug_id(IrExecutable *exec) {
|
|
size_t result = exec->next_debug_id;
|
|
exec->next_debug_id += 1;
|
|
return result;
|
|
}
|
|
|
|
static size_t exec_next_mem_slot(IrExecutable *exec) {
|
|
size_t result = exec->mem_slot_count;
|
|
exec->mem_slot_count += 1;
|
|
return result;
|
|
}
|
|
|
|
static ZigFn *exec_fn_entry(IrExecutable *exec) {
|
|
return exec->fn_entry;
|
|
}
|
|
|
|
static Buf *exec_c_import_buf(IrExecutable *exec) {
|
|
return exec->c_import_buf;
|
|
}
|
|
|
|
static bool value_is_comptime(ConstExprValue *const_val) {
|
|
return const_val->special != ConstValSpecialRuntime;
|
|
}
|
|
|
|
static bool instr_is_comptime(IrInstruction *instruction) {
|
|
return value_is_comptime(&instruction->value);
|
|
}
|
|
|
|
static bool instr_is_unreachable(IrInstruction *instruction) {
|
|
return instruction->value.type && instruction->value.type->id == ZigTypeIdUnreachable;
|
|
}
|
|
|
|
static void ir_link_new_bb(IrBasicBlock *new_bb, IrBasicBlock *old_bb) {
|
|
new_bb->other = old_bb;
|
|
old_bb->other = new_bb;
|
|
}
|
|
|
|
static void ir_ref_bb(IrBasicBlock *bb) {
|
|
bb->ref_count += 1;
|
|
}
|
|
|
|
static void ir_ref_instruction(IrInstruction *instruction, IrBasicBlock *cur_bb) {
|
|
assert(instruction->id != IrInstructionIdInvalid);
|
|
instruction->ref_count += 1;
|
|
if (instruction->owner_bb != cur_bb && !instr_is_comptime(instruction))
|
|
ir_ref_bb(instruction->owner_bb);
|
|
}
|
|
|
|
static void ir_ref_var(ZigVar *var) {
|
|
var->ref_count += 1;
|
|
}
|
|
|
|
ZigType *ir_analyze_type_expr(IrAnalyze *ira, Scope *scope, AstNode *node) {
|
|
ConstExprValue *result = ir_eval_const_value(ira->codegen, scope, node, ira->codegen->builtin_types.entry_type,
|
|
ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, nullptr, nullptr,
|
|
node, nullptr, ira->new_irb.exec, nullptr);
|
|
|
|
if (type_is_invalid(result->type))
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
|
|
assert(result->special != ConstValSpecialRuntime);
|
|
return result->data.x_type;
|
|
}
|
|
|
|
static IrBasicBlock *ir_create_basic_block(IrBuilder *irb, Scope *scope, const char *name_hint) {
|
|
IrBasicBlock *result = allocate<IrBasicBlock>(1);
|
|
result->scope = scope;
|
|
result->name_hint = name_hint;
|
|
result->debug_id = exec_next_debug_id(irb->exec);
|
|
result->index = SIZE_MAX; // set later
|
|
return result;
|
|
}
|
|
|
|
static IrBasicBlock *ir_build_bb_from(IrBuilder *irb, IrBasicBlock *other_bb) {
|
|
IrBasicBlock *new_bb = ir_create_basic_block(irb, other_bb->scope, other_bb->name_hint);
|
|
ir_link_new_bb(new_bb, other_bb);
|
|
return new_bb;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclVarSrc *) {
|
|
return IrInstructionIdDeclVarSrc;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclVarGen *) {
|
|
return IrInstructionIdDeclVarGen;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionCondBr *) {
|
|
return IrInstructionIdCondBr;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionBr *) {
|
|
return IrInstructionIdBr;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionSwitchBr *) {
|
|
return IrInstructionIdSwitchBr;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionSwitchVar *) {
|
|
return IrInstructionIdSwitchVar;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionSwitchElseVar *) {
|
|
return IrInstructionIdSwitchElseVar;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionSwitchTarget *) {
|
|
return IrInstructionIdSwitchTarget;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionPhi *) {
|
|
return IrInstructionIdPhi;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionUnOp *) {
|
|
return IrInstructionIdUnOp;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionBinOp *) {
|
|
return IrInstructionIdBinOp;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionExport *) {
|
|
return IrInstructionIdExport;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionLoadPtr *) {
|
|
return IrInstructionIdLoadPtr;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionLoadPtrGen *) {
|
|
return IrInstructionIdLoadPtrGen;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionStorePtr *) {
|
|
return IrInstructionIdStorePtr;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionFieldPtr *) {
|
|
return IrInstructionIdFieldPtr;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionStructFieldPtr *) {
|
|
return IrInstructionIdStructFieldPtr;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionUnionFieldPtr *) {
|
|
return IrInstructionIdUnionFieldPtr;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionElemPtr *) {
|
|
return IrInstructionIdElemPtr;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionVarPtr *) {
|
|
return IrInstructionIdVarPtr;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionReturnPtr *) {
|
|
return IrInstructionIdReturnPtr;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionCallSrc *) {
|
|
return IrInstructionIdCallSrc;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionCallGen *) {
|
|
return IrInstructionIdCallGen;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionConst *) {
|
|
return IrInstructionIdConst;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionReturn *) {
|
|
return IrInstructionIdReturn;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionReturnBegin *) {
|
|
return IrInstructionIdReturnBegin;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionCast *) {
|
|
return IrInstructionIdCast;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionResizeSlice *) {
|
|
return IrInstructionIdResizeSlice;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionContainerInitList *) {
|
|
return IrInstructionIdContainerInitList;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionContainerInitFields *) {
|
|
return IrInstructionIdContainerInitFields;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionUnreachable *) {
|
|
return IrInstructionIdUnreachable;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeOf *) {
|
|
return IrInstructionIdTypeOf;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionSetCold *) {
|
|
return IrInstructionIdSetCold;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionSetRuntimeSafety *) {
|
|
return IrInstructionIdSetRuntimeSafety;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionSetFloatMode *) {
|
|
return IrInstructionIdSetFloatMode;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayType *) {
|
|
return IrInstructionIdArrayType;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionAnyFrameType *) {
|
|
return IrInstructionIdAnyFrameType;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionSliceType *) {
|
|
return IrInstructionIdSliceType;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionGlobalAsm *) {
|
|
return IrInstructionIdGlobalAsm;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionAsm *) {
|
|
return IrInstructionIdAsm;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionSizeOf *) {
|
|
return IrInstructionIdSizeOf;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionTestNonNull *) {
|
|
return IrInstructionIdTestNonNull;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionOptionalUnwrapPtr *) {
|
|
return IrInstructionIdOptionalUnwrapPtr;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionClz *) {
|
|
return IrInstructionIdClz;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionCtz *) {
|
|
return IrInstructionIdCtz;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionPopCount *) {
|
|
return IrInstructionIdPopCount;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionBswap *) {
|
|
return IrInstructionIdBswap;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionBitReverse *) {
|
|
return IrInstructionIdBitReverse;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionUnionTag *) {
|
|
return IrInstructionIdUnionTag;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionImport *) {
|
|
return IrInstructionIdImport;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionCImport *) {
|
|
return IrInstructionIdCImport;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionCInclude *) {
|
|
return IrInstructionIdCInclude;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionCDefine *) {
|
|
return IrInstructionIdCDefine;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionCUndef *) {
|
|
return IrInstructionIdCUndef;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionRef *) {
|
|
return IrInstructionIdRef;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionRefGen *) {
|
|
return IrInstructionIdRefGen;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionCompileErr *) {
|
|
return IrInstructionIdCompileErr;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionCompileLog *) {
|
|
return IrInstructionIdCompileLog;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionErrName *) {
|
|
return IrInstructionIdErrName;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionEmbedFile *) {
|
|
return IrInstructionIdEmbedFile;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionCmpxchgSrc *) {
|
|
return IrInstructionIdCmpxchgSrc;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionCmpxchgGen *) {
|
|
return IrInstructionIdCmpxchgGen;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionFence *) {
|
|
return IrInstructionIdFence;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionTruncate *) {
|
|
return IrInstructionIdTruncate;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionIntCast *) {
|
|
return IrInstructionIdIntCast;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionFloatCast *) {
|
|
return IrInstructionIdFloatCast;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionErrSetCast *) {
|
|
return IrInstructionIdErrSetCast;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionToBytes *) {
|
|
return IrInstructionIdToBytes;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionFromBytes *) {
|
|
return IrInstructionIdFromBytes;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionIntToFloat *) {
|
|
return IrInstructionIdIntToFloat;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionFloatToInt *) {
|
|
return IrInstructionIdFloatToInt;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionBoolToInt *) {
|
|
return IrInstructionIdBoolToInt;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionIntType *) {
|
|
return IrInstructionIdIntType;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionVectorType *) {
|
|
return IrInstructionIdVectorType;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionBoolNot *) {
|
|
return IrInstructionIdBoolNot;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionMemset *) {
|
|
return IrInstructionIdMemset;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionMemcpy *) {
|
|
return IrInstructionIdMemcpy;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionSliceSrc *) {
|
|
return IrInstructionIdSliceSrc;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionSliceGen *) {
|
|
return IrInstructionIdSliceGen;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionMemberCount *) {
|
|
return IrInstructionIdMemberCount;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionMemberType *) {
|
|
return IrInstructionIdMemberType;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionMemberName *) {
|
|
return IrInstructionIdMemberName;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionBreakpoint *) {
|
|
return IrInstructionIdBreakpoint;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionReturnAddress *) {
|
|
return IrInstructionIdReturnAddress;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionFrameAddress *) {
|
|
return IrInstructionIdFrameAddress;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionFrameHandle *) {
|
|
return IrInstructionIdFrameHandle;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionFrameType *) {
|
|
return IrInstructionIdFrameType;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionFrameSizeSrc *) {
|
|
return IrInstructionIdFrameSizeSrc;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionFrameSizeGen *) {
|
|
return IrInstructionIdFrameSizeGen;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionAlignOf *) {
|
|
return IrInstructionIdAlignOf;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionOverflowOp *) {
|
|
return IrInstructionIdOverflowOp;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionTestErrSrc *) {
|
|
return IrInstructionIdTestErrSrc;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionTestErrGen *) {
|
|
return IrInstructionIdTestErrGen;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionMulAdd *) {
|
|
return IrInstructionIdMulAdd;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapErrCode *) {
|
|
return IrInstructionIdUnwrapErrCode;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapErrPayload *) {
|
|
return IrInstructionIdUnwrapErrPayload;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionOptionalWrap *) {
|
|
return IrInstructionIdOptionalWrap;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionErrWrapPayload *) {
|
|
return IrInstructionIdErrWrapPayload;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionErrWrapCode *) {
|
|
return IrInstructionIdErrWrapCode;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionFnProto *) {
|
|
return IrInstructionIdFnProto;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionTestComptime *) {
|
|
return IrInstructionIdTestComptime;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrCastSrc *) {
|
|
return IrInstructionIdPtrCastSrc;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrCastGen *) {
|
|
return IrInstructionIdPtrCastGen;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionBitCastSrc *) {
|
|
return IrInstructionIdBitCastSrc;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionBitCastGen *) {
|
|
return IrInstructionIdBitCastGen;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionWidenOrShorten *) {
|
|
return IrInstructionIdWidenOrShorten;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrToInt *) {
|
|
return IrInstructionIdPtrToInt;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionIntToPtr *) {
|
|
return IrInstructionIdIntToPtr;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionIntToEnum *) {
|
|
return IrInstructionIdIntToEnum;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionEnumToInt *) {
|
|
return IrInstructionIdEnumToInt;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionIntToErr *) {
|
|
return IrInstructionIdIntToErr;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionErrToInt *) {
|
|
return IrInstructionIdErrToInt;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckSwitchProngs *) {
|
|
return IrInstructionIdCheckSwitchProngs;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckStatementIsVoid *) {
|
|
return IrInstructionIdCheckStatementIsVoid;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeName *) {
|
|
return IrInstructionIdTypeName;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclRef *) {
|
|
return IrInstructionIdDeclRef;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionPanic *) {
|
|
return IrInstructionIdPanic;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionTagName *) {
|
|
return IrInstructionIdTagName;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionTagType *) {
|
|
return IrInstructionIdTagType;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionFieldParentPtr *) {
|
|
return IrInstructionIdFieldParentPtr;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionByteOffsetOf *) {
|
|
return IrInstructionIdByteOffsetOf;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionBitOffsetOf *) {
|
|
return IrInstructionIdBitOffsetOf;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeInfo *) {
|
|
return IrInstructionIdTypeInfo;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionHasField *) {
|
|
return IrInstructionIdHasField;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeId *) {
|
|
return IrInstructionIdTypeId;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionSetEvalBranchQuota *) {
|
|
return IrInstructionIdSetEvalBranchQuota;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrType *) {
|
|
return IrInstructionIdPtrType;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionAlignCast *) {
|
|
return IrInstructionIdAlignCast;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionImplicitCast *) {
|
|
return IrInstructionIdImplicitCast;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionResolveResult *) {
|
|
return IrInstructionIdResolveResult;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionResetResult *) {
|
|
return IrInstructionIdResetResult;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrOfArrayToSlice *) {
|
|
return IrInstructionIdPtrOfArrayToSlice;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionOpaqueType *) {
|
|
return IrInstructionIdOpaqueType;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionSetAlignStack *) {
|
|
return IrInstructionIdSetAlignStack;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionArgType *) {
|
|
return IrInstructionIdArgType;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorReturnTrace *) {
|
|
return IrInstructionIdErrorReturnTrace;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorUnion *) {
|
|
return IrInstructionIdErrorUnion;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionCancel *) {
|
|
return IrInstructionIdCancel;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicRmw *) {
|
|
return IrInstructionIdAtomicRmw;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicLoad *) {
|
|
return IrInstructionIdAtomicLoad;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionSaveErrRetAddr *) {
|
|
return IrInstructionIdSaveErrRetAddr;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionAddImplicitReturnType *) {
|
|
return IrInstructionIdAddImplicitReturnType;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionFloatOp *) {
|
|
return IrInstructionIdFloatOp;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckRuntimeScope *) {
|
|
return IrInstructionIdCheckRuntimeScope;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionVectorToArray *) {
|
|
return IrInstructionIdVectorToArray;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayToVector *) {
|
|
return IrInstructionIdArrayToVector;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionAssertZero *) {
|
|
return IrInstructionIdAssertZero;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionAssertNonNull *) {
|
|
return IrInstructionIdAssertNonNull;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionHasDecl *) {
|
|
return IrInstructionIdHasDecl;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionUndeclaredIdent *) {
|
|
return IrInstructionIdUndeclaredIdent;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionAllocaSrc *) {
|
|
return IrInstructionIdAllocaSrc;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionAllocaGen *) {
|
|
return IrInstructionIdAllocaGen;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionEndExpr *) {
|
|
return IrInstructionIdEndExpr;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionUnionInitNamedField *) {
|
|
return IrInstructionIdUnionInitNamedField;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionSuspendBegin *) {
|
|
return IrInstructionIdSuspendBegin;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionSuspendFinish *) {
|
|
return IrInstructionIdSuspendFinish;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionAwaitSrc *) {
|
|
return IrInstructionIdAwaitSrc;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionAwaitGen *) {
|
|
return IrInstructionIdAwaitGen;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroResume *) {
|
|
return IrInstructionIdCoroResume;
|
|
}
|
|
|
|
template<typename T>
|
|
static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
|
|
T *special_instruction = allocate<T>(1);
|
|
special_instruction->base.id = ir_instruction_id(special_instruction);
|
|
special_instruction->base.scope = scope;
|
|
special_instruction->base.source_node = source_node;
|
|
special_instruction->base.debug_id = exec_next_debug_id(irb->exec);
|
|
special_instruction->base.owner_bb = irb->current_basic_block;
|
|
special_instruction->base.value.global_refs = allocate<ConstGlobalRefs>(1);
|
|
return special_instruction;
|
|
}
|
|
|
|
template<typename T>
|
|
static T *ir_build_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
|
|
T *special_instruction = ir_create_instruction<T>(irb, scope, source_node);
|
|
ir_instruction_append(irb->current_basic_block, &special_instruction->base);
|
|
return special_instruction;
|
|
}
|
|
|
|
static IrInstruction *ir_build_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigType *dest_type,
|
|
IrInstruction *value, CastOp cast_op)
|
|
{
|
|
IrInstructionCast *cast_instruction = ir_build_instruction<IrInstructionCast>(irb, scope, source_node);
|
|
cast_instruction->dest_type = dest_type;
|
|
cast_instruction->value = value;
|
|
cast_instruction->cast_op = cast_op;
|
|
|
|
ir_ref_instruction(value, irb->current_basic_block);
|
|
|
|
return &cast_instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_cond_br(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *condition,
|
|
IrBasicBlock *then_block, IrBasicBlock *else_block, IrInstruction *is_comptime)
|
|
{
|
|
IrInstructionCondBr *cond_br_instruction = ir_build_instruction<IrInstructionCondBr>(irb, scope, source_node);
|
|
cond_br_instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable;
|
|
cond_br_instruction->base.value.special = ConstValSpecialStatic;
|
|
cond_br_instruction->condition = condition;
|
|
cond_br_instruction->then_block = then_block;
|
|
cond_br_instruction->else_block = else_block;
|
|
cond_br_instruction->is_comptime = is_comptime;
|
|
|
|
ir_ref_instruction(condition, irb->current_basic_block);
|
|
ir_ref_bb(then_block);
|
|
ir_ref_bb(else_block);
|
|
if (is_comptime != nullptr) ir_ref_instruction(is_comptime, irb->current_basic_block);
|
|
|
|
return &cond_br_instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_return(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *operand)
|
|
{
|
|
IrInstructionReturn *return_instruction = ir_build_instruction<IrInstructionReturn>(irb, scope, source_node);
|
|
return_instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable;
|
|
return_instruction->base.value.special = ConstValSpecialStatic;
|
|
return_instruction->operand = operand;
|
|
|
|
if (operand != nullptr) ir_ref_instruction(operand, irb->current_basic_block);
|
|
|
|
return &return_instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_return_begin(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *operand)
|
|
{
|
|
IrInstructionReturnBegin *return_instruction = ir_build_instruction<IrInstructionReturnBegin>(irb, scope, source_node);
|
|
return_instruction->base.value.type = irb->codegen->builtin_types.entry_void;
|
|
return_instruction->base.value.special = ConstValSpecialStatic;
|
|
return_instruction->operand = operand;
|
|
|
|
ir_ref_instruction(operand, irb->current_basic_block);
|
|
|
|
return &return_instruction->base;
|
|
}
|
|
|
|
|
|
static IrInstruction *ir_build_const_void(IrBuilder *irb, Scope *scope, AstNode *source_node) {
|
|
IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
|
|
const_instruction->base.value.type = irb->codegen->builtin_types.entry_void;
|
|
const_instruction->base.value.special = ConstValSpecialStatic;
|
|
return &const_instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_const_undefined(IrBuilder *irb, Scope *scope, AstNode *source_node) {
|
|
IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
|
|
const_instruction->base.value.special = ConstValSpecialUndef;
|
|
const_instruction->base.value.type = irb->codegen->builtin_types.entry_undef;
|
|
return &const_instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_const_uint(IrBuilder *irb, Scope *scope, AstNode *source_node, uint64_t value) {
|
|
IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
|
|
const_instruction->base.value.type = irb->codegen->builtin_types.entry_num_lit_int;
|
|
const_instruction->base.value.special = ConstValSpecialStatic;
|
|
bigint_init_unsigned(&const_instruction->base.value.data.x_bigint, value);
|
|
return &const_instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_const_bigint(IrBuilder *irb, Scope *scope, AstNode *source_node, BigInt *bigint) {
|
|
IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
|
|
const_instruction->base.value.type = irb->codegen->builtin_types.entry_num_lit_int;
|
|
const_instruction->base.value.special = ConstValSpecialStatic;
|
|
bigint_init_bigint(&const_instruction->base.value.data.x_bigint, bigint);
|
|
return &const_instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_const_bigfloat(IrBuilder *irb, Scope *scope, AstNode *source_node, BigFloat *bigfloat) {
|
|
IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
|
|
const_instruction->base.value.type = irb->codegen->builtin_types.entry_num_lit_float;
|
|
const_instruction->base.value.special = ConstValSpecialStatic;
|
|
bigfloat_init_bigfloat(&const_instruction->base.value.data.x_bigfloat, bigfloat);
|
|
return &const_instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_const_null(IrBuilder *irb, Scope *scope, AstNode *source_node) {
|
|
IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
|
|
const_instruction->base.value.type = irb->codegen->builtin_types.entry_null;
|
|
const_instruction->base.value.special = ConstValSpecialStatic;
|
|
return &const_instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_const_usize(IrBuilder *irb, Scope *scope, AstNode *source_node, uint64_t value) {
|
|
IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
|
|
const_instruction->base.value.type = irb->codegen->builtin_types.entry_usize;
|
|
const_instruction->base.value.special = ConstValSpecialStatic;
|
|
bigint_init_unsigned(&const_instruction->base.value.data.x_bigint, value);
|
|
return &const_instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_create_const_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
ZigType *type_entry)
|
|
{
|
|
IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(irb, scope, source_node);
|
|
const_instruction->base.value.type = irb->codegen->builtin_types.entry_type;
|
|
const_instruction->base.value.special = ConstValSpecialStatic;
|
|
const_instruction->base.value.data.x_type = type_entry;
|
|
return &const_instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_const_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
ZigType *type_entry)
|
|
{
|
|
IrInstruction *instruction = ir_create_const_type(irb, scope, source_node, type_entry);
|
|
ir_instruction_append(irb->current_basic_block, instruction);
|
|
return instruction;
|
|
}
|
|
|
|
static IrInstruction *ir_create_const_fn(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigFn *fn_entry) {
|
|
IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(irb, scope, source_node);
|
|
const_instruction->base.value.type = fn_entry->type_entry;
|
|
const_instruction->base.value.special = ConstValSpecialStatic;
|
|
const_instruction->base.value.data.x_ptr.data.fn.fn_entry = fn_entry;
|
|
const_instruction->base.value.data.x_ptr.mut = ConstPtrMutComptimeConst;
|
|
const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialFunction;
|
|
return &const_instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_const_import(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigType *import) {
|
|
IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
|
|
const_instruction->base.value.type = irb->codegen->builtin_types.entry_type;
|
|
const_instruction->base.value.special = ConstValSpecialStatic;
|
|
const_instruction->base.value.data.x_type = import;
|
|
return &const_instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_const_bool(IrBuilder *irb, Scope *scope, AstNode *source_node, bool value) {
|
|
IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
|
|
const_instruction->base.value.type = irb->codegen->builtin_types.entry_bool;
|
|
const_instruction->base.value.special = ConstValSpecialStatic;
|
|
const_instruction->base.value.data.x_bool = value;
|
|
return &const_instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_const_enum_literal(IrBuilder *irb, Scope *scope, AstNode *source_node, Buf *name) {
|
|
IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
|
|
const_instruction->base.value.type = irb->codegen->builtin_types.entry_enum_literal;
|
|
const_instruction->base.value.special = ConstValSpecialStatic;
|
|
const_instruction->base.value.data.x_enum_literal = name;
|
|
return &const_instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_const_bound_fn(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
ZigFn *fn_entry, IrInstruction *first_arg)
|
|
{
|
|
IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
|
|
const_instruction->base.value.type = get_bound_fn_type(irb->codegen, fn_entry);
|
|
const_instruction->base.value.special = ConstValSpecialStatic;
|
|
const_instruction->base.value.data.x_bound_fn.fn = fn_entry;
|
|
const_instruction->base.value.data.x_bound_fn.first_arg = first_arg;
|
|
return &const_instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_create_const_str_lit(IrBuilder *irb, Scope *scope, AstNode *source_node, Buf *str) {
|
|
IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(irb, scope, source_node);
|
|
init_const_str_lit(irb->codegen, &const_instruction->base.value, str);
|
|
|
|
return &const_instruction->base;
|
|
}
|
|
static IrInstruction *ir_build_const_str_lit(IrBuilder *irb, Scope *scope, AstNode *source_node, Buf *str) {
|
|
IrInstruction *instruction = ir_create_const_str_lit(irb, scope, source_node, str);
|
|
ir_instruction_append(irb->current_basic_block, instruction);
|
|
return instruction;
|
|
}
|
|
|
|
static IrInstruction *ir_build_const_c_str_lit(IrBuilder *irb, Scope *scope, AstNode *source_node, Buf *str) {
|
|
IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
|
|
init_const_c_str_lit(irb->codegen, &const_instruction->base.value, str);
|
|
return &const_instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_bin_op(IrBuilder *irb, Scope *scope, AstNode *source_node, IrBinOp op_id,
|
|
IrInstruction *op1, IrInstruction *op2, bool safety_check_on)
|
|
{
|
|
IrInstructionBinOp *bin_op_instruction = ir_build_instruction<IrInstructionBinOp>(irb, scope, source_node);
|
|
bin_op_instruction->op_id = op_id;
|
|
bin_op_instruction->op1 = op1;
|
|
bin_op_instruction->op2 = op2;
|
|
bin_op_instruction->safety_check_on = safety_check_on;
|
|
|
|
ir_ref_instruction(op1, irb->current_basic_block);
|
|
ir_ref_instruction(op2, irb->current_basic_block);
|
|
|
|
return &bin_op_instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_var_ptr_x(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigVar *var,
|
|
ScopeFnDef *crossed_fndef_scope)
|
|
{
|
|
IrInstructionVarPtr *instruction = ir_build_instruction<IrInstructionVarPtr>(irb, scope, source_node);
|
|
instruction->var = var;
|
|
instruction->crossed_fndef_scope = crossed_fndef_scope;
|
|
|
|
ir_ref_var(var);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_var_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigVar *var) {
|
|
return ir_build_var_ptr_x(irb, scope, source_node, var, nullptr);
|
|
}
|
|
|
|
static IrInstruction *ir_build_return_ptr(IrAnalyze *ira, IrInstruction *source_instruction, ZigType *ty) {
|
|
IrInstructionReturnPtr *instruction = ir_build_instruction<IrInstructionReturnPtr>(&ira->new_irb,
|
|
source_instruction->scope, source_instruction->source_node);
|
|
instruction->base.value.type = ty;
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_elem_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *array_ptr, IrInstruction *elem_index, bool safety_check_on, PtrLen ptr_len,
|
|
IrInstruction *init_array_type)
|
|
{
|
|
IrInstructionElemPtr *instruction = ir_build_instruction<IrInstructionElemPtr>(irb, scope, source_node);
|
|
instruction->array_ptr = array_ptr;
|
|
instruction->elem_index = elem_index;
|
|
instruction->safety_check_on = safety_check_on;
|
|
instruction->ptr_len = ptr_len;
|
|
instruction->init_array_type = init_array_type;
|
|
|
|
ir_ref_instruction(array_ptr, irb->current_basic_block);
|
|
ir_ref_instruction(elem_index, irb->current_basic_block);
|
|
if (init_array_type != nullptr) ir_ref_instruction(init_array_type, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_field_ptr_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *container_ptr, IrInstruction *field_name_expr, bool initializing)
|
|
{
|
|
IrInstructionFieldPtr *instruction = ir_build_instruction<IrInstructionFieldPtr>(irb, scope, source_node);
|
|
instruction->container_ptr = container_ptr;
|
|
instruction->field_name_buffer = nullptr;
|
|
instruction->field_name_expr = field_name_expr;
|
|
instruction->initializing = initializing;
|
|
|
|
ir_ref_instruction(container_ptr, irb->current_basic_block);
|
|
ir_ref_instruction(field_name_expr, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *container_ptr, Buf *field_name, bool initializing)
|
|
{
|
|
IrInstructionFieldPtr *instruction = ir_build_instruction<IrInstructionFieldPtr>(irb, scope, source_node);
|
|
instruction->container_ptr = container_ptr;
|
|
instruction->field_name_buffer = field_name;
|
|
instruction->field_name_expr = nullptr;
|
|
instruction->initializing = initializing;
|
|
|
|
ir_ref_instruction(container_ptr, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_has_field(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *container_type, IrInstruction *field_name)
|
|
{
|
|
IrInstructionHasField *instruction = ir_build_instruction<IrInstructionHasField>(irb, scope, source_node);
|
|
instruction->container_type = container_type;
|
|
instruction->field_name = field_name;
|
|
|
|
ir_ref_instruction(container_type, irb->current_basic_block);
|
|
ir_ref_instruction(field_name, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_struct_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *struct_ptr, TypeStructField *field)
|
|
{
|
|
IrInstructionStructFieldPtr *instruction = ir_build_instruction<IrInstructionStructFieldPtr>(irb, scope, source_node);
|
|
instruction->struct_ptr = struct_ptr;
|
|
instruction->field = field;
|
|
|
|
ir_ref_instruction(struct_ptr, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_union_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *union_ptr, TypeUnionField *field, bool safety_check_on, bool initializing)
|
|
{
|
|
IrInstructionUnionFieldPtr *instruction = ir_build_instruction<IrInstructionUnionFieldPtr>(irb, scope, source_node);
|
|
instruction->initializing = initializing;
|
|
instruction->safety_check_on = safety_check_on;
|
|
instruction->union_ptr = union_ptr;
|
|
instruction->field = field;
|
|
|
|
ir_ref_instruction(union_ptr, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_call_src(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
ZigFn *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args,
|
|
bool is_comptime, FnInline fn_inline, bool is_async,
|
|
IrInstruction *new_stack, ResultLoc *result_loc)
|
|
{
|
|
IrInstructionCallSrc *call_instruction = ir_build_instruction<IrInstructionCallSrc>(irb, scope, source_node);
|
|
call_instruction->fn_entry = fn_entry;
|
|
call_instruction->fn_ref = fn_ref;
|
|
call_instruction->is_comptime = is_comptime;
|
|
call_instruction->fn_inline = fn_inline;
|
|
call_instruction->args = args;
|
|
call_instruction->arg_count = arg_count;
|
|
call_instruction->is_async = is_async;
|
|
call_instruction->new_stack = new_stack;
|
|
call_instruction->result_loc = result_loc;
|
|
|
|
if (fn_ref != nullptr) ir_ref_instruction(fn_ref, irb->current_basic_block);
|
|
for (size_t i = 0; i < arg_count; i += 1)
|
|
ir_ref_instruction(args[i], irb->current_basic_block);
|
|
if (is_async && new_stack != nullptr) {
|
|
// in this case the arg at the end is the return pointer
|
|
ir_ref_instruction(args[arg_count], irb->current_basic_block);
|
|
}
|
|
if (new_stack != nullptr) ir_ref_instruction(new_stack, irb->current_basic_block);
|
|
|
|
return &call_instruction->base;
|
|
}
|
|
|
|
static IrInstructionCallGen *ir_build_call_gen(IrAnalyze *ira, IrInstruction *source_instruction,
|
|
ZigFn *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args,
|
|
FnInline fn_inline, bool is_async, IrInstruction *new_stack,
|
|
IrInstruction *result_loc, ZigType *return_type)
|
|
{
|
|
IrInstructionCallGen *call_instruction = ir_build_instruction<IrInstructionCallGen>(&ira->new_irb,
|
|
source_instruction->scope, source_instruction->source_node);
|
|
call_instruction->base.value.type = return_type;
|
|
call_instruction->fn_entry = fn_entry;
|
|
call_instruction->fn_ref = fn_ref;
|
|
call_instruction->fn_inline = fn_inline;
|
|
call_instruction->args = args;
|
|
call_instruction->arg_count = arg_count;
|
|
call_instruction->is_async = is_async;
|
|
call_instruction->new_stack = new_stack;
|
|
call_instruction->result_loc = result_loc;
|
|
|
|
if (fn_ref != nullptr) ir_ref_instruction(fn_ref, ira->new_irb.current_basic_block);
|
|
for (size_t i = 0; i < arg_count; i += 1)
|
|
ir_ref_instruction(args[i], ira->new_irb.current_basic_block);
|
|
if (new_stack != nullptr) ir_ref_instruction(new_stack, ira->new_irb.current_basic_block);
|
|
if (result_loc != nullptr) ir_ref_instruction(result_loc, ira->new_irb.current_basic_block);
|
|
|
|
return call_instruction;
|
|
}
|
|
|
|
static IrInstruction *ir_build_phi(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
size_t incoming_count, IrBasicBlock **incoming_blocks, IrInstruction **incoming_values,
|
|
ResultLocPeerParent *peer_parent)
|
|
{
|
|
assert(incoming_count != 0);
|
|
assert(incoming_count != SIZE_MAX);
|
|
|
|
IrInstructionPhi *phi_instruction = ir_build_instruction<IrInstructionPhi>(irb, scope, source_node);
|
|
phi_instruction->incoming_count = incoming_count;
|
|
phi_instruction->incoming_blocks = incoming_blocks;
|
|
phi_instruction->incoming_values = incoming_values;
|
|
phi_instruction->peer_parent = peer_parent;
|
|
|
|
for (size_t i = 0; i < incoming_count; i += 1) {
|
|
ir_ref_bb(incoming_blocks[i]);
|
|
ir_ref_instruction(incoming_values[i], irb->current_basic_block);
|
|
}
|
|
|
|
return &phi_instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_create_br(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrBasicBlock *dest_block, IrInstruction *is_comptime)
|
|
{
|
|
IrInstructionBr *br_instruction = ir_create_instruction<IrInstructionBr>(irb, scope, source_node);
|
|
br_instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable;
|
|
br_instruction->base.value.special = ConstValSpecialStatic;
|
|
br_instruction->dest_block = dest_block;
|
|
br_instruction->is_comptime = is_comptime;
|
|
|
|
ir_ref_bb(dest_block);
|
|
if (is_comptime) ir_ref_instruction(is_comptime, irb->current_basic_block);
|
|
|
|
return &br_instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_br(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrBasicBlock *dest_block, IrInstruction *is_comptime)
|
|
{
|
|
IrInstruction *instruction = ir_create_br(irb, scope, source_node, dest_block, is_comptime);
|
|
ir_instruction_append(irb->current_basic_block, instruction);
|
|
return instruction;
|
|
}
|
|
|
|
static IrInstruction *ir_build_ptr_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *child_type, bool is_const, bool is_volatile, PtrLen ptr_len,
|
|
IrInstruction *align_value, uint32_t bit_offset_start, uint32_t host_int_bytes, bool is_allow_zero)
|
|
{
|
|
IrInstructionPtrType *ptr_type_of_instruction = ir_build_instruction<IrInstructionPtrType>(irb, scope, source_node);
|
|
ptr_type_of_instruction->align_value = align_value;
|
|
ptr_type_of_instruction->child_type = child_type;
|
|
ptr_type_of_instruction->is_const = is_const;
|
|
ptr_type_of_instruction->is_volatile = is_volatile;
|
|
ptr_type_of_instruction->ptr_len = ptr_len;
|
|
ptr_type_of_instruction->bit_offset_start = bit_offset_start;
|
|
ptr_type_of_instruction->host_int_bytes = host_int_bytes;
|
|
ptr_type_of_instruction->is_allow_zero = is_allow_zero;
|
|
|
|
if (align_value) ir_ref_instruction(align_value, irb->current_basic_block);
|
|
ir_ref_instruction(child_type, irb->current_basic_block);
|
|
|
|
return &ptr_type_of_instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_un_op_lval(IrBuilder *irb, Scope *scope, AstNode *source_node, IrUnOp op_id,
|
|
IrInstruction *value, LVal lval, ResultLoc *result_loc)
|
|
{
|
|
IrInstructionUnOp *instruction = ir_build_instruction<IrInstructionUnOp>(irb, scope, source_node);
|
|
instruction->op_id = op_id;
|
|
instruction->value = value;
|
|
instruction->lval = lval;
|
|
instruction->result_loc = result_loc;
|
|
|
|
ir_ref_instruction(value, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_un_op(IrBuilder *irb, Scope *scope, AstNode *source_node, IrUnOp op_id,
|
|
IrInstruction *value)
|
|
{
|
|
return ir_build_un_op_lval(irb, scope, source_node, op_id, value, LValNone, nullptr);
|
|
}
|
|
|
|
static IrInstruction *ir_build_container_init_list(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *container_type, size_t item_count, IrInstruction **elem_result_loc_list,
|
|
IrInstruction *result_loc)
|
|
{
|
|
IrInstructionContainerInitList *container_init_list_instruction =
|
|
ir_build_instruction<IrInstructionContainerInitList>(irb, scope, source_node);
|
|
container_init_list_instruction->container_type = container_type;
|
|
container_init_list_instruction->item_count = item_count;
|
|
container_init_list_instruction->elem_result_loc_list = elem_result_loc_list;
|
|
container_init_list_instruction->result_loc = result_loc;
|
|
|
|
ir_ref_instruction(container_type, irb->current_basic_block);
|
|
for (size_t i = 0; i < item_count; i += 1) {
|
|
ir_ref_instruction(elem_result_loc_list[i], irb->current_basic_block);
|
|
}
|
|
if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block);
|
|
|
|
return &container_init_list_instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_container_init_fields(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *container_type, size_t field_count, IrInstructionContainerInitFieldsField *fields,
|
|
IrInstruction *result_loc)
|
|
{
|
|
IrInstructionContainerInitFields *container_init_fields_instruction =
|
|
ir_build_instruction<IrInstructionContainerInitFields>(irb, scope, source_node);
|
|
container_init_fields_instruction->container_type = container_type;
|
|
container_init_fields_instruction->field_count = field_count;
|
|
container_init_fields_instruction->fields = fields;
|
|
container_init_fields_instruction->result_loc = result_loc;
|
|
|
|
ir_ref_instruction(container_type, irb->current_basic_block);
|
|
for (size_t i = 0; i < field_count; i += 1) {
|
|
ir_ref_instruction(fields[i].result_loc, irb->current_basic_block);
|
|
}
|
|
if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block);
|
|
|
|
return &container_init_fields_instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_unreachable(IrBuilder *irb, Scope *scope, AstNode *source_node) {
|
|
IrInstructionUnreachable *unreachable_instruction =
|
|
ir_build_instruction<IrInstructionUnreachable>(irb, scope, source_node);
|
|
unreachable_instruction->base.value.special = ConstValSpecialStatic;
|
|
unreachable_instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable;
|
|
return &unreachable_instruction->base;
|
|
}
|
|
|
|
static IrInstructionStorePtr *ir_build_store_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *ptr, IrInstruction *value)
|
|
{
|
|
IrInstructionStorePtr *instruction = ir_build_instruction<IrInstructionStorePtr>(irb, scope, source_node);
|
|
instruction->base.value.special = ConstValSpecialStatic;
|
|
instruction->base.value.type = irb->codegen->builtin_types.entry_void;
|
|
instruction->ptr = ptr;
|
|
instruction->value = value;
|
|
|
|
ir_ref_instruction(ptr, irb->current_basic_block);
|
|
ir_ref_instruction(value, irb->current_basic_block);
|
|
|
|
return instruction;
|
|
}
|
|
|
|
static IrInstruction *ir_build_var_decl_src(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
ZigVar *var, IrInstruction *align_value, IrInstruction *ptr)
|
|
{
|
|
IrInstructionDeclVarSrc *decl_var_instruction = ir_build_instruction<IrInstructionDeclVarSrc>(irb, scope, source_node);
|
|
decl_var_instruction->base.value.special = ConstValSpecialStatic;
|
|
decl_var_instruction->base.value.type = irb->codegen->builtin_types.entry_void;
|
|
decl_var_instruction->var = var;
|
|
decl_var_instruction->align_value = align_value;
|
|
decl_var_instruction->ptr = ptr;
|
|
|
|
if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block);
|
|
ir_ref_instruction(ptr, irb->current_basic_block);
|
|
|
|
return &decl_var_instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_var_decl_gen(IrAnalyze *ira, IrInstruction *source_instruction,
|
|
ZigVar *var, IrInstruction *var_ptr)
|
|
{
|
|
IrInstructionDeclVarGen *decl_var_instruction = ir_build_instruction<IrInstructionDeclVarGen>(&ira->new_irb,
|
|
source_instruction->scope, source_instruction->source_node);
|
|
decl_var_instruction->base.value.special = ConstValSpecialStatic;
|
|
decl_var_instruction->base.value.type = ira->codegen->builtin_types.entry_void;
|
|
decl_var_instruction->var = var;
|
|
decl_var_instruction->var_ptr = var_ptr;
|
|
|
|
ir_ref_instruction(var_ptr, ira->new_irb.current_basic_block);
|
|
|
|
return &decl_var_instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_resize_slice(IrAnalyze *ira, IrInstruction *source_instruction,
|
|
IrInstruction *operand, ZigType *ty, IrInstruction *result_loc)
|
|
{
|
|
IrInstructionResizeSlice *instruction = ir_build_instruction<IrInstructionResizeSlice>(&ira->new_irb,
|
|
source_instruction->scope, source_instruction->source_node);
|
|
instruction->base.value.type = ty;
|
|
instruction->operand = operand;
|
|
instruction->result_loc = result_loc;
|
|
|
|
ir_ref_instruction(operand, ira->new_irb.current_basic_block);
|
|
ir_ref_instruction(result_loc, ira->new_irb.current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_export(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *name, IrInstruction *target, IrInstruction *linkage)
|
|
{
|
|
IrInstructionExport *export_instruction = ir_build_instruction<IrInstructionExport>(
|
|
irb, scope, source_node);
|
|
export_instruction->base.value.special = ConstValSpecialStatic;
|
|
export_instruction->base.value.type = irb->codegen->builtin_types.entry_void;
|
|
export_instruction->name = name;
|
|
export_instruction->target = target;
|
|
export_instruction->linkage = linkage;
|
|
|
|
ir_ref_instruction(name, irb->current_basic_block);
|
|
ir_ref_instruction(target, irb->current_basic_block);
|
|
if (linkage) ir_ref_instruction(linkage, irb->current_basic_block);
|
|
|
|
return &export_instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_load_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *ptr) {
|
|
IrInstructionLoadPtr *instruction = ir_build_instruction<IrInstructionLoadPtr>(irb, scope, source_node);
|
|
instruction->ptr = ptr;
|
|
|
|
ir_ref_instruction(ptr, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_typeof(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
|
|
IrInstructionTypeOf *instruction = ir_build_instruction<IrInstructionTypeOf>(irb, scope, source_node);
|
|
instruction->value = value;
|
|
|
|
ir_ref_instruction(value, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_set_cold(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *is_cold) {
|
|
IrInstructionSetCold *instruction = ir_build_instruction<IrInstructionSetCold>(irb, scope, source_node);
|
|
instruction->is_cold = is_cold;
|
|
|
|
ir_ref_instruction(is_cold, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_set_runtime_safety(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *safety_on)
|
|
{
|
|
IrInstructionSetRuntimeSafety *instruction = ir_build_instruction<IrInstructionSetRuntimeSafety>(irb, scope, source_node);
|
|
instruction->safety_on = safety_on;
|
|
|
|
ir_ref_instruction(safety_on, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_set_float_mode(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *mode_value)
|
|
{
|
|
IrInstructionSetFloatMode *instruction = ir_build_instruction<IrInstructionSetFloatMode>(irb, scope, source_node);
|
|
instruction->mode_value = mode_value;
|
|
|
|
ir_ref_instruction(mode_value, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_array_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *size,
|
|
IrInstruction *child_type)
|
|
{
|
|
IrInstructionArrayType *instruction = ir_build_instruction<IrInstructionArrayType>(irb, scope, source_node);
|
|
instruction->size = size;
|
|
instruction->child_type = child_type;
|
|
|
|
ir_ref_instruction(size, irb->current_basic_block);
|
|
ir_ref_instruction(child_type, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_anyframe_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *payload_type)
|
|
{
|
|
IrInstructionAnyFrameType *instruction = ir_build_instruction<IrInstructionAnyFrameType>(irb, scope, source_node);
|
|
instruction->payload_type = payload_type;
|
|
|
|
if (payload_type != nullptr) ir_ref_instruction(payload_type, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
static IrInstruction *ir_build_slice_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *child_type, bool is_const, bool is_volatile, IrInstruction *align_value, bool is_allow_zero)
|
|
{
|
|
IrInstructionSliceType *instruction = ir_build_instruction<IrInstructionSliceType>(irb, scope, source_node);
|
|
instruction->is_const = is_const;
|
|
instruction->is_volatile = is_volatile;
|
|
instruction->child_type = child_type;
|
|
instruction->align_value = align_value;
|
|
instruction->is_allow_zero = is_allow_zero;
|
|
|
|
ir_ref_instruction(child_type, irb->current_basic_block);
|
|
if (align_value) ir_ref_instruction(align_value, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_global_asm(IrBuilder *irb, Scope *scope, AstNode *source_node, Buf *asm_code) {
|
|
IrInstructionGlobalAsm *instruction = ir_build_instruction<IrInstructionGlobalAsm>(irb, scope, source_node);
|
|
instruction->asm_code = asm_code;
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_asm(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
Buf *asm_template, AsmToken *token_list, size_t token_list_len,
|
|
IrInstruction **input_list, IrInstruction **output_types, ZigVar **output_vars, size_t return_count,
|
|
bool has_side_effects)
|
|
{
|
|
IrInstructionAsm *instruction = ir_build_instruction<IrInstructionAsm>(irb, scope, source_node);
|
|
instruction->asm_template = asm_template;
|
|
instruction->token_list = token_list;
|
|
instruction->token_list_len = token_list_len;
|
|
instruction->input_list = input_list;
|
|
instruction->output_types = output_types;
|
|
instruction->output_vars = output_vars;
|
|
instruction->return_count = return_count;
|
|
instruction->has_side_effects = has_side_effects;
|
|
|
|
assert(source_node->type == NodeTypeAsmExpr);
|
|
for (size_t i = 0; i < source_node->data.asm_expr.output_list.length; i += 1) {
|
|
IrInstruction *output_type = output_types[i];
|
|
if (output_type) ir_ref_instruction(output_type, irb->current_basic_block);
|
|
}
|
|
|
|
for (size_t i = 0; i < source_node->data.asm_expr.input_list.length; i += 1) {
|
|
IrInstruction *input_value = input_list[i];
|
|
ir_ref_instruction(input_value, irb->current_basic_block);
|
|
}
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_size_of(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value) {
|
|
IrInstructionSizeOf *instruction = ir_build_instruction<IrInstructionSizeOf>(irb, scope, source_node);
|
|
instruction->type_value = type_value;
|
|
|
|
ir_ref_instruction(type_value, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_test_nonnull(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
|
|
IrInstructionTestNonNull *instruction = ir_build_instruction<IrInstructionTestNonNull>(irb, scope, source_node);
|
|
instruction->value = value;
|
|
|
|
ir_ref_instruction(value, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_optional_unwrap_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *base_ptr, bool safety_check_on, bool initializing)
|
|
{
|
|
IrInstructionOptionalUnwrapPtr *instruction = ir_build_instruction<IrInstructionOptionalUnwrapPtr>(irb, scope, source_node);
|
|
instruction->base_ptr = base_ptr;
|
|
instruction->safety_check_on = safety_check_on;
|
|
instruction->initializing = initializing;
|
|
|
|
ir_ref_instruction(base_ptr, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_optional_wrap(IrAnalyze *ira, IrInstruction *source_instruction, ZigType *result_ty,
|
|
IrInstruction *operand, IrInstruction *result_loc)
|
|
{
|
|
IrInstructionOptionalWrap *instruction = ir_build_instruction<IrInstructionOptionalWrap>(
|
|
&ira->new_irb, source_instruction->scope, source_instruction->source_node);
|
|
instruction->base.value.type = result_ty;
|
|
instruction->operand = operand;
|
|
instruction->result_loc = result_loc;
|
|
|
|
ir_ref_instruction(operand, ira->new_irb.current_basic_block);
|
|
if (result_loc != nullptr) ir_ref_instruction(result_loc, ira->new_irb.current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_err_wrap_payload(IrAnalyze *ira, IrInstruction *source_instruction,
|
|
ZigType *result_type, IrInstruction *operand, IrInstruction *result_loc)
|
|
{
|
|
IrInstructionErrWrapPayload *instruction = ir_build_instruction<IrInstructionErrWrapPayload>(
|
|
&ira->new_irb, source_instruction->scope, source_instruction->source_node);
|
|
instruction->base.value.type = result_type;
|
|
instruction->operand = operand;
|
|
instruction->result_loc = result_loc;
|
|
|
|
ir_ref_instruction(operand, ira->new_irb.current_basic_block);
|
|
if (result_loc != nullptr) ir_ref_instruction(result_loc, ira->new_irb.current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_err_wrap_code(IrAnalyze *ira, IrInstruction *source_instruction,
|
|
ZigType *result_type, IrInstruction *operand, IrInstruction *result_loc)
|
|
{
|
|
IrInstructionErrWrapCode *instruction = ir_build_instruction<IrInstructionErrWrapCode>(
|
|
&ira->new_irb, source_instruction->scope, source_instruction->source_node);
|
|
instruction->base.value.type = result_type;
|
|
instruction->operand = operand;
|
|
instruction->result_loc = result_loc;
|
|
|
|
ir_ref_instruction(operand, ira->new_irb.current_basic_block);
|
|
if (result_loc != nullptr) ir_ref_instruction(result_loc, ira->new_irb.current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_clz(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type, IrInstruction *op) {
|
|
IrInstructionClz *instruction = ir_build_instruction<IrInstructionClz>(irb, scope, source_node);
|
|
instruction->type = type;
|
|
instruction->op = op;
|
|
|
|
if (type != nullptr) ir_ref_instruction(type, irb->current_basic_block);
|
|
ir_ref_instruction(op, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_ctz(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type, IrInstruction *op) {
|
|
IrInstructionCtz *instruction = ir_build_instruction<IrInstructionCtz>(irb, scope, source_node);
|
|
instruction->type = type;
|
|
instruction->op = op;
|
|
|
|
if (type != nullptr) ir_ref_instruction(type, irb->current_basic_block);
|
|
ir_ref_instruction(op, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_pop_count(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type, IrInstruction *op) {
|
|
IrInstructionPopCount *instruction = ir_build_instruction<IrInstructionPopCount>(irb, scope, source_node);
|
|
instruction->type = type;
|
|
instruction->op = op;
|
|
|
|
if (type != nullptr) ir_ref_instruction(type, irb->current_basic_block);
|
|
ir_ref_instruction(op, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_bswap(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type, IrInstruction *op) {
|
|
IrInstructionBswap *instruction = ir_build_instruction<IrInstructionBswap>(irb, scope, source_node);
|
|
instruction->type = type;
|
|
instruction->op = op;
|
|
|
|
if (type != nullptr) ir_ref_instruction(type, irb->current_basic_block);
|
|
ir_ref_instruction(op, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_bit_reverse(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type, IrInstruction *op) {
|
|
IrInstructionBitReverse *instruction = ir_build_instruction<IrInstructionBitReverse>(irb, scope, source_node);
|
|
instruction->type = type;
|
|
instruction->op = op;
|
|
|
|
if (type != nullptr) ir_ref_instruction(type, irb->current_basic_block);
|
|
ir_ref_instruction(op, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstructionSwitchBr *ir_build_switch_br(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target_value,
|
|
IrBasicBlock *else_block, size_t case_count, IrInstructionSwitchBrCase *cases, IrInstruction *is_comptime,
|
|
IrInstruction *switch_prongs_void)
|
|
{
|
|
IrInstructionSwitchBr *instruction = ir_build_instruction<IrInstructionSwitchBr>(irb, scope, source_node);
|
|
instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable;
|
|
instruction->base.value.special = ConstValSpecialStatic;
|
|
instruction->target_value = target_value;
|
|
instruction->else_block = else_block;
|
|
instruction->case_count = case_count;
|
|
instruction->cases = cases;
|
|
instruction->is_comptime = is_comptime;
|
|
instruction->switch_prongs_void = switch_prongs_void;
|
|
|
|
ir_ref_instruction(target_value, irb->current_basic_block);
|
|
if (is_comptime) ir_ref_instruction(is_comptime, irb->current_basic_block);
|
|
ir_ref_bb(else_block);
|
|
if (switch_prongs_void) ir_ref_instruction(switch_prongs_void, irb->current_basic_block);
|
|
|
|
for (size_t i = 0; i < case_count; i += 1) {
|
|
ir_ref_instruction(cases[i].value, irb->current_basic_block);
|
|
ir_ref_bb(cases[i].block);
|
|
}
|
|
|
|
return instruction;
|
|
}
|
|
|
|
static IrInstruction *ir_build_switch_target(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *target_value_ptr)
|
|
{
|
|
IrInstructionSwitchTarget *instruction = ir_build_instruction<IrInstructionSwitchTarget>(irb, scope, source_node);
|
|
instruction->target_value_ptr = target_value_ptr;
|
|
|
|
ir_ref_instruction(target_value_ptr, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_switch_var(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *target_value_ptr, IrInstruction **prongs_ptr, size_t prongs_len)
|
|
{
|
|
IrInstructionSwitchVar *instruction = ir_build_instruction<IrInstructionSwitchVar>(irb, scope, source_node);
|
|
instruction->target_value_ptr = target_value_ptr;
|
|
instruction->prongs_ptr = prongs_ptr;
|
|
instruction->prongs_len = prongs_len;
|
|
|
|
ir_ref_instruction(target_value_ptr, irb->current_basic_block);
|
|
for (size_t i = 0; i < prongs_len; i += 1) {
|
|
ir_ref_instruction(prongs_ptr[i], irb->current_basic_block);
|
|
}
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
// For this instruction the switch_br must be set later.
|
|
static IrInstructionSwitchElseVar *ir_build_switch_else_var(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *target_value_ptr)
|
|
{
|
|
IrInstructionSwitchElseVar *instruction = ir_build_instruction<IrInstructionSwitchElseVar>(irb, scope, source_node);
|
|
instruction->target_value_ptr = target_value_ptr;
|
|
|
|
ir_ref_instruction(target_value_ptr, irb->current_basic_block);
|
|
|
|
return instruction;
|
|
}
|
|
|
|
static IrInstruction *ir_build_union_tag(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
|
|
IrInstructionUnionTag *instruction = ir_build_instruction<IrInstructionUnionTag>(irb, scope, source_node);
|
|
instruction->value = value;
|
|
|
|
ir_ref_instruction(value, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_import(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *name) {
|
|
IrInstructionImport *instruction = ir_build_instruction<IrInstructionImport>(irb, scope, source_node);
|
|
instruction->name = name;
|
|
|
|
ir_ref_instruction(name, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_ref(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value,
|
|
bool is_const, bool is_volatile)
|
|
{
|
|
IrInstructionRef *instruction = ir_build_instruction<IrInstructionRef>(irb, scope, source_node);
|
|
instruction->value = value;
|
|
instruction->is_const = is_const;
|
|
instruction->is_volatile = is_volatile;
|
|
|
|
ir_ref_instruction(value, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_ref_gen(IrAnalyze *ira, IrInstruction *source_instruction, ZigType *result_type,
|
|
IrInstruction *operand, IrInstruction *result_loc)
|
|
{
|
|
IrInstructionRefGen *instruction = ir_build_instruction<IrInstructionRefGen>(&ira->new_irb,
|
|
source_instruction->scope, source_instruction->source_node);
|
|
instruction->base.value.type = result_type;
|
|
instruction->operand = operand;
|
|
instruction->result_loc = result_loc;
|
|
|
|
ir_ref_instruction(operand, ira->new_irb.current_basic_block);
|
|
if (result_loc != nullptr) ir_ref_instruction(result_loc, ira->new_irb.current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_compile_err(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *msg) {
|
|
IrInstructionCompileErr *instruction = ir_build_instruction<IrInstructionCompileErr>(irb, scope, source_node);
|
|
instruction->msg = msg;
|
|
|
|
ir_ref_instruction(msg, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_compile_log(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
size_t msg_count, IrInstruction **msg_list)
|
|
{
|
|
IrInstructionCompileLog *instruction = ir_build_instruction<IrInstructionCompileLog>(irb, scope, source_node);
|
|
instruction->msg_count = msg_count;
|
|
instruction->msg_list = msg_list;
|
|
|
|
for (size_t i = 0; i < msg_count; i += 1) {
|
|
ir_ref_instruction(msg_list[i], irb->current_basic_block);
|
|
}
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_err_name(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
|
|
IrInstructionErrName *instruction = ir_build_instruction<IrInstructionErrName>(irb, scope, source_node);
|
|
instruction->value = value;
|
|
|
|
ir_ref_instruction(value, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_c_import(IrBuilder *irb, Scope *scope, AstNode *source_node) {
|
|
IrInstructionCImport *instruction = ir_build_instruction<IrInstructionCImport>(irb, scope, source_node);
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_c_include(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *name) {
|
|
IrInstructionCInclude *instruction = ir_build_instruction<IrInstructionCInclude>(irb, scope, source_node);
|
|
instruction->name = name;
|
|
|
|
ir_ref_instruction(name, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_c_define(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *name, IrInstruction *value) {
|
|
IrInstructionCDefine *instruction = ir_build_instruction<IrInstructionCDefine>(irb, scope, source_node);
|
|
instruction->name = name;
|
|
instruction->value = value;
|
|
|
|
ir_ref_instruction(name, irb->current_basic_block);
|
|
ir_ref_instruction(value, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_c_undef(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *name) {
|
|
IrInstructionCUndef *instruction = ir_build_instruction<IrInstructionCUndef>(irb, scope, source_node);
|
|
instruction->name = name;
|
|
|
|
ir_ref_instruction(name, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_embed_file(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *name) {
|
|
IrInstructionEmbedFile *instruction = ir_build_instruction<IrInstructionEmbedFile>(irb, scope, source_node);
|
|
instruction->name = name;
|
|
|
|
ir_ref_instruction(name, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_cmpxchg_src(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *type_value, IrInstruction *ptr, IrInstruction *cmp_value, IrInstruction *new_value,
|
|
IrInstruction *success_order_value, IrInstruction *failure_order_value, bool is_weak, ResultLoc *result_loc)
|
|
{
|
|
IrInstructionCmpxchgSrc *instruction = ir_build_instruction<IrInstructionCmpxchgSrc>(irb, scope, source_node);
|
|
instruction->type_value = type_value;
|
|
instruction->ptr = ptr;
|
|
instruction->cmp_value = cmp_value;
|
|
instruction->new_value = new_value;
|
|
instruction->success_order_value = success_order_value;
|
|
instruction->failure_order_value = failure_order_value;
|
|
instruction->is_weak = is_weak;
|
|
instruction->result_loc = result_loc;
|
|
|
|
ir_ref_instruction(type_value, irb->current_basic_block);
|
|
ir_ref_instruction(ptr, irb->current_basic_block);
|
|
ir_ref_instruction(cmp_value, irb->current_basic_block);
|
|
ir_ref_instruction(new_value, irb->current_basic_block);
|
|
ir_ref_instruction(success_order_value, irb->current_basic_block);
|
|
ir_ref_instruction(failure_order_value, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_cmpxchg_gen(IrAnalyze *ira, IrInstruction *source_instruction, ZigType *result_type,
|
|
IrInstruction *ptr, IrInstruction *cmp_value, IrInstruction *new_value,
|
|
AtomicOrder success_order, AtomicOrder failure_order, bool is_weak, IrInstruction *result_loc)
|
|
{
|
|
IrInstructionCmpxchgGen *instruction = ir_build_instruction<IrInstructionCmpxchgGen>(&ira->new_irb,
|
|
source_instruction->scope, source_instruction->source_node);
|
|
instruction->base.value.type = result_type;
|
|
instruction->ptr = ptr;
|
|
instruction->cmp_value = cmp_value;
|
|
instruction->new_value = new_value;
|
|
instruction->success_order = success_order;
|
|
instruction->failure_order = failure_order;
|
|
instruction->is_weak = is_weak;
|
|
instruction->result_loc = result_loc;
|
|
|
|
ir_ref_instruction(ptr, ira->new_irb.current_basic_block);
|
|
ir_ref_instruction(cmp_value, ira->new_irb.current_basic_block);
|
|
ir_ref_instruction(new_value, ira->new_irb.current_basic_block);
|
|
if (result_loc != nullptr) ir_ref_instruction(result_loc, ira->new_irb.current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_fence(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *order_value, AtomicOrder order) {
|
|
IrInstructionFence *instruction = ir_build_instruction<IrInstructionFence>(irb, scope, source_node);
|
|
instruction->order_value = order_value;
|
|
instruction->order = order;
|
|
|
|
ir_ref_instruction(order_value, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_truncate(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) {
|
|
IrInstructionTruncate *instruction = ir_build_instruction<IrInstructionTruncate>(irb, scope, source_node);
|
|
instruction->dest_type = dest_type;
|
|
instruction->target = target;
|
|
|
|
ir_ref_instruction(dest_type, irb->current_basic_block);
|
|
ir_ref_instruction(target, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_int_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) {
|
|
IrInstructionIntCast *instruction = ir_build_instruction<IrInstructionIntCast>(irb, scope, source_node);
|
|
instruction->dest_type = dest_type;
|
|
instruction->target = target;
|
|
|
|
ir_ref_instruction(dest_type, irb->current_basic_block);
|
|
ir_ref_instruction(target, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_float_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) {
|
|
IrInstructionFloatCast *instruction = ir_build_instruction<IrInstructionFloatCast>(irb, scope, source_node);
|
|
instruction->dest_type = dest_type;
|
|
instruction->target = target;
|
|
|
|
ir_ref_instruction(dest_type, irb->current_basic_block);
|
|
ir_ref_instruction(target, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_err_set_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) {
|
|
IrInstructionErrSetCast *instruction = ir_build_instruction<IrInstructionErrSetCast>(irb, scope, source_node);
|
|
instruction->dest_type = dest_type;
|
|
instruction->target = target;
|
|
|
|
ir_ref_instruction(dest_type, irb->current_basic_block);
|
|
ir_ref_instruction(target, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_to_bytes(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target,
|
|
ResultLoc *result_loc)
|
|
{
|
|
IrInstructionToBytes *instruction = ir_build_instruction<IrInstructionToBytes>(irb, scope, source_node);
|
|
instruction->target = target;
|
|
instruction->result_loc = result_loc;
|
|
|
|
ir_ref_instruction(target, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_from_bytes(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *dest_child_type, IrInstruction *target, ResultLoc *result_loc)
|
|
{
|
|
IrInstructionFromBytes *instruction = ir_build_instruction<IrInstructionFromBytes>(irb, scope, source_node);
|
|
instruction->dest_child_type = dest_child_type;
|
|
instruction->target = target;
|
|
instruction->result_loc = result_loc;
|
|
|
|
ir_ref_instruction(dest_child_type, irb->current_basic_block);
|
|
ir_ref_instruction(target, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_int_to_float(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) {
|
|
IrInstructionIntToFloat *instruction = ir_build_instruction<IrInstructionIntToFloat>(irb, scope, source_node);
|
|
instruction->dest_type = dest_type;
|
|
instruction->target = target;
|
|
|
|
ir_ref_instruction(dest_type, irb->current_basic_block);
|
|
ir_ref_instruction(target, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_float_to_int(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *dest_type, IrInstruction *target) {
|
|
IrInstructionFloatToInt *instruction = ir_build_instruction<IrInstructionFloatToInt>(irb, scope, source_node);
|
|
instruction->dest_type = dest_type;
|
|
instruction->target = target;
|
|
|
|
ir_ref_instruction(dest_type, irb->current_basic_block);
|
|
ir_ref_instruction(target, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_bool_to_int(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target) {
|
|
IrInstructionBoolToInt *instruction = ir_build_instruction<IrInstructionBoolToInt>(irb, scope, source_node);
|
|
instruction->target = target;
|
|
|
|
ir_ref_instruction(target, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_int_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *is_signed, IrInstruction *bit_count) {
|
|
IrInstructionIntType *instruction = ir_build_instruction<IrInstructionIntType>(irb, scope, source_node);
|
|
instruction->is_signed = is_signed;
|
|
instruction->bit_count = bit_count;
|
|
|
|
ir_ref_instruction(is_signed, irb->current_basic_block);
|
|
ir_ref_instruction(bit_count, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_vector_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *len,
|
|
IrInstruction *elem_type)
|
|
{
|
|
IrInstructionVectorType *instruction = ir_build_instruction<IrInstructionVectorType>(irb, scope, source_node);
|
|
instruction->len = len;
|
|
instruction->elem_type = elem_type;
|
|
|
|
ir_ref_instruction(len, irb->current_basic_block);
|
|
ir_ref_instruction(elem_type, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_bool_not(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
|
|
IrInstructionBoolNot *instruction = ir_build_instruction<IrInstructionBoolNot>(irb, scope, source_node);
|
|
instruction->value = value;
|
|
|
|
ir_ref_instruction(value, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_memset(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *dest_ptr, IrInstruction *byte, IrInstruction *count)
|
|
{
|
|
IrInstructionMemset *instruction = ir_build_instruction<IrInstructionMemset>(irb, scope, source_node);
|
|
instruction->dest_ptr = dest_ptr;
|
|
instruction->byte = byte;
|
|
instruction->count = count;
|
|
|
|
ir_ref_instruction(dest_ptr, irb->current_basic_block);
|
|
ir_ref_instruction(byte, irb->current_basic_block);
|
|
ir_ref_instruction(count, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_memcpy(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *dest_ptr, IrInstruction *src_ptr, IrInstruction *count)
|
|
{
|
|
IrInstructionMemcpy *instruction = ir_build_instruction<IrInstructionMemcpy>(irb, scope, source_node);
|
|
instruction->dest_ptr = dest_ptr;
|
|
instruction->src_ptr = src_ptr;
|
|
instruction->count = count;
|
|
|
|
ir_ref_instruction(dest_ptr, irb->current_basic_block);
|
|
ir_ref_instruction(src_ptr, irb->current_basic_block);
|
|
ir_ref_instruction(count, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_slice_src(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *ptr, IrInstruction *start, IrInstruction *end, bool safety_check_on, ResultLoc *result_loc)
|
|
{
|
|
IrInstructionSliceSrc *instruction = ir_build_instruction<IrInstructionSliceSrc>(irb, scope, source_node);
|
|
instruction->ptr = ptr;
|
|
instruction->start = start;
|
|
instruction->end = end;
|
|
instruction->safety_check_on = safety_check_on;
|
|
instruction->result_loc = result_loc;
|
|
|
|
ir_ref_instruction(ptr, irb->current_basic_block);
|
|
ir_ref_instruction(start, irb->current_basic_block);
|
|
if (end) ir_ref_instruction(end, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_slice_gen(IrAnalyze *ira, IrInstruction *source_instruction, ZigType *slice_type,
|
|
IrInstruction *ptr, IrInstruction *start, IrInstruction *end, bool safety_check_on, IrInstruction *result_loc)
|
|
{
|
|
IrInstructionSliceGen *instruction = ir_build_instruction<IrInstructionSliceGen>(
|
|
&ira->new_irb, source_instruction->scope, source_instruction->source_node);
|
|
instruction->base.value.type = slice_type;
|
|
instruction->ptr = ptr;
|
|
instruction->start = start;
|
|
instruction->end = end;
|
|
instruction->safety_check_on = safety_check_on;
|
|
instruction->result_loc = result_loc;
|
|
|
|
ir_ref_instruction(ptr, ira->new_irb.current_basic_block);
|
|
ir_ref_instruction(start, ira->new_irb.current_basic_block);
|
|
if (end) ir_ref_instruction(end, ira->new_irb.current_basic_block);
|
|
ir_ref_instruction(result_loc, ira->new_irb.current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_member_count(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *container) {
|
|
IrInstructionMemberCount *instruction = ir_build_instruction<IrInstructionMemberCount>(irb, scope, source_node);
|
|
instruction->container = container;
|
|
|
|
ir_ref_instruction(container, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_member_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *container_type, IrInstruction *member_index)
|
|
{
|
|
IrInstructionMemberType *instruction = ir_build_instruction<IrInstructionMemberType>(irb, scope, source_node);
|
|
instruction->container_type = container_type;
|
|
instruction->member_index = member_index;
|
|
|
|
ir_ref_instruction(container_type, irb->current_basic_block);
|
|
ir_ref_instruction(member_index, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_member_name(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *container_type, IrInstruction *member_index)
|
|
{
|
|
IrInstructionMemberName *instruction = ir_build_instruction<IrInstructionMemberName>(irb, scope, source_node);
|
|
instruction->container_type = container_type;
|
|
instruction->member_index = member_index;
|
|
|
|
ir_ref_instruction(container_type, irb->current_basic_block);
|
|
ir_ref_instruction(member_index, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_breakpoint(IrBuilder *irb, Scope *scope, AstNode *source_node) {
|
|
IrInstructionBreakpoint *instruction = ir_build_instruction<IrInstructionBreakpoint>(irb, scope, source_node);
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_return_address(IrBuilder *irb, Scope *scope, AstNode *source_node) {
|
|
IrInstructionReturnAddress *instruction = ir_build_instruction<IrInstructionReturnAddress>(irb, scope, source_node);
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_frame_address(IrBuilder *irb, Scope *scope, AstNode *source_node) {
|
|
IrInstructionFrameAddress *instruction = ir_build_instruction<IrInstructionFrameAddress>(irb, scope, source_node);
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_handle(IrBuilder *irb, Scope *scope, AstNode *source_node) {
|
|
IrInstructionFrameHandle *instruction = ir_build_instruction<IrInstructionFrameHandle>(irb, scope, source_node);
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_frame_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *fn) {
|
|
IrInstructionFrameType *instruction = ir_build_instruction<IrInstructionFrameType>(irb, scope, source_node);
|
|
instruction->fn = fn;
|
|
|
|
ir_ref_instruction(fn, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_frame_size_src(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *fn) {
|
|
IrInstructionFrameSizeSrc *instruction = ir_build_instruction<IrInstructionFrameSizeSrc>(irb, scope, source_node);
|
|
instruction->fn = fn;
|
|
|
|
ir_ref_instruction(fn, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_frame_size_gen(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *fn)
|
|
{
|
|
IrInstructionFrameSizeGen *instruction = ir_build_instruction<IrInstructionFrameSizeGen>(irb, scope, source_node);
|
|
instruction->fn = fn;
|
|
|
|
ir_ref_instruction(fn, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_overflow_op(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrOverflowOp op, IrInstruction *type_value, IrInstruction *op1, IrInstruction *op2,
|
|
IrInstruction *result_ptr, ZigType *result_ptr_type)
|
|
{
|
|
IrInstructionOverflowOp *instruction = ir_build_instruction<IrInstructionOverflowOp>(irb, scope, source_node);
|
|
instruction->op = op;
|
|
instruction->type_value = type_value;
|
|
instruction->op1 = op1;
|
|
instruction->op2 = op2;
|
|
instruction->result_ptr = result_ptr;
|
|
instruction->result_ptr_type = result_ptr_type;
|
|
|
|
ir_ref_instruction(type_value, irb->current_basic_block);
|
|
ir_ref_instruction(op1, irb->current_basic_block);
|
|
ir_ref_instruction(op2, irb->current_basic_block);
|
|
ir_ref_instruction(result_ptr, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
|
|
//TODO Powi, Pow, minnum, maxnum, maximum, minimum, copysign,
|
|
// lround, llround, lrint, llrint
|
|
// So far this is only non-complicated type functions.
|
|
const char *float_op_to_name(BuiltinFnId op, bool llvm_name) {
|
|
const bool b = llvm_name;
|
|
|
|
switch (op) {
|
|
case BuiltinFnIdSqrt:
|
|
return "sqrt";
|
|
case BuiltinFnIdSin:
|
|
return "sin";
|
|
case BuiltinFnIdCos:
|
|
return "cos";
|
|
case BuiltinFnIdExp:
|
|
return "exp";
|
|
case BuiltinFnIdExp2:
|
|
return "exp2";
|
|
case BuiltinFnIdLn:
|
|
return b ? "log" : "ln";
|
|
case BuiltinFnIdLog10:
|
|
return "log10";
|
|
case BuiltinFnIdLog2:
|
|
return "log2";
|
|
case BuiltinFnIdFabs:
|
|
return "fabs";
|
|
case BuiltinFnIdFloor:
|
|
return "floor";
|
|
case BuiltinFnIdCeil:
|
|
return "ceil";
|
|
case BuiltinFnIdTrunc:
|
|
return "trunc";
|
|
case BuiltinFnIdNearbyInt:
|
|
return b ? "nearbyint" : "nearbyInt";
|
|
case BuiltinFnIdRound:
|
|
return "round";
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
|
|
static IrInstruction *ir_build_float_op(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type, IrInstruction *op1, BuiltinFnId op) {
|
|
IrInstructionFloatOp *instruction = ir_build_instruction<IrInstructionFloatOp>(irb, scope, source_node);
|
|
instruction->type = type;
|
|
instruction->op1 = op1;
|
|
instruction->op = op;
|
|
|
|
if (type != nullptr) ir_ref_instruction(type, irb->current_basic_block);
|
|
ir_ref_instruction(op1, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_mul_add(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *type_value, IrInstruction *op1, IrInstruction *op2, IrInstruction *op3) {
|
|
IrInstructionMulAdd *instruction = ir_build_instruction<IrInstructionMulAdd>(irb, scope, source_node);
|
|
instruction->type_value = type_value;
|
|
instruction->op1 = op1;
|
|
instruction->op2 = op2;
|
|
instruction->op3 = op3;
|
|
|
|
ir_ref_instruction(type_value, irb->current_basic_block);
|
|
ir_ref_instruction(op1, irb->current_basic_block);
|
|
ir_ref_instruction(op2, irb->current_basic_block);
|
|
ir_ref_instruction(op3, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_align_of(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value) {
|
|
IrInstructionAlignOf *instruction = ir_build_instruction<IrInstructionAlignOf>(irb, scope, source_node);
|
|
instruction->type_value = type_value;
|
|
|
|
ir_ref_instruction(type_value, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_test_err_src(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *base_ptr, bool resolve_err_set, bool base_ptr_is_payload)
|
|
{
|
|
IrInstructionTestErrSrc *instruction = ir_build_instruction<IrInstructionTestErrSrc>(irb, scope, source_node);
|
|
instruction->base_ptr = base_ptr;
|
|
instruction->resolve_err_set = resolve_err_set;
|
|
instruction->base_ptr_is_payload = base_ptr_is_payload;
|
|
|
|
ir_ref_instruction(base_ptr, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_test_err_gen(IrAnalyze *ira, IrInstruction *source_instruction,
|
|
IrInstruction *err_union)
|
|
{
|
|
IrInstructionTestErrGen *instruction = ir_build_instruction<IrInstructionTestErrGen>(
|
|
&ira->new_irb, source_instruction->scope, source_instruction->source_node);
|
|
instruction->base.value.type = ira->codegen->builtin_types.entry_bool;
|
|
instruction->err_union = err_union;
|
|
|
|
ir_ref_instruction(err_union, ira->new_irb.current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_unwrap_err_code(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *err_union_ptr)
|
|
{
|
|
IrInstructionUnwrapErrCode *instruction = ir_build_instruction<IrInstructionUnwrapErrCode>(irb, scope, source_node);
|
|
instruction->err_union_ptr = err_union_ptr;
|
|
|
|
ir_ref_instruction(err_union_ptr, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_unwrap_err_payload(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *value, bool safety_check_on, bool initializing)
|
|
{
|
|
IrInstructionUnwrapErrPayload *instruction = ir_build_instruction<IrInstructionUnwrapErrPayload>(irb, scope, source_node);
|
|
instruction->value = value;
|
|
instruction->safety_check_on = safety_check_on;
|
|
instruction->initializing = initializing;
|
|
|
|
ir_ref_instruction(value, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_fn_proto(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction **param_types, IrInstruction *align_value, IrInstruction *return_type,
|
|
bool is_var_args)
|
|
{
|
|
IrInstructionFnProto *instruction = ir_build_instruction<IrInstructionFnProto>(irb, scope, source_node);
|
|
instruction->param_types = param_types;
|
|
instruction->align_value = align_value;
|
|
instruction->return_type = return_type;
|
|
instruction->is_var_args = is_var_args;
|
|
|
|
assert(source_node->type == NodeTypeFnProto);
|
|
size_t param_count = source_node->data.fn_proto.params.length;
|
|
if (is_var_args) param_count -= 1;
|
|
for (size_t i = 0; i < param_count; i += 1) {
|
|
if (param_types[i] != nullptr) ir_ref_instruction(param_types[i], irb->current_basic_block);
|
|
}
|
|
if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block);
|
|
ir_ref_instruction(return_type, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_test_comptime(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
|
|
IrInstructionTestComptime *instruction = ir_build_instruction<IrInstructionTestComptime>(irb, scope, source_node);
|
|
instruction->value = value;
|
|
|
|
ir_ref_instruction(value, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_ptr_cast_src(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *dest_type, IrInstruction *ptr, bool safety_check_on)
|
|
{
|
|
IrInstructionPtrCastSrc *instruction = ir_build_instruction<IrInstructionPtrCastSrc>(
|
|
irb, scope, source_node);
|
|
instruction->dest_type = dest_type;
|
|
instruction->ptr = ptr;
|
|
instruction->safety_check_on = safety_check_on;
|
|
|
|
ir_ref_instruction(dest_type, irb->current_basic_block);
|
|
ir_ref_instruction(ptr, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_ptr_cast_gen(IrAnalyze *ira, IrInstruction *source_instruction,
|
|
ZigType *ptr_type, IrInstruction *ptr, bool safety_check_on)
|
|
{
|
|
IrInstructionPtrCastGen *instruction = ir_build_instruction<IrInstructionPtrCastGen>(
|
|
&ira->new_irb, source_instruction->scope, source_instruction->source_node);
|
|
instruction->base.value.type = ptr_type;
|
|
instruction->ptr = ptr;
|
|
instruction->safety_check_on = safety_check_on;
|
|
|
|
ir_ref_instruction(ptr, ira->new_irb.current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_load_ptr_gen(IrAnalyze *ira, IrInstruction *source_instruction,
|
|
IrInstruction *ptr, ZigType *ty, IrInstruction *result_loc)
|
|
{
|
|
IrInstructionLoadPtrGen *instruction = ir_build_instruction<IrInstructionLoadPtrGen>(
|
|
&ira->new_irb, source_instruction->scope, source_instruction->source_node);
|
|
instruction->base.value.type = ty;
|
|
instruction->ptr = ptr;
|
|
instruction->result_loc = result_loc;
|
|
|
|
ir_ref_instruction(ptr, ira->new_irb.current_basic_block);
|
|
if (result_loc != nullptr) ir_ref_instruction(result_loc, ira->new_irb.current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_bit_cast_src(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *operand, ResultLocBitCast *result_loc_bit_cast)
|
|
{
|
|
IrInstructionBitCastSrc *instruction = ir_build_instruction<IrInstructionBitCastSrc>(irb, scope, source_node);
|
|
instruction->operand = operand;
|
|
instruction->result_loc_bit_cast = result_loc_bit_cast;
|
|
|
|
ir_ref_instruction(operand, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_bit_cast_gen(IrAnalyze *ira, IrInstruction *source_instruction,
|
|
IrInstruction *operand, ZigType *ty)
|
|
{
|
|
IrInstructionBitCastGen *instruction = ir_build_instruction<IrInstructionBitCastGen>(
|
|
&ira->new_irb, source_instruction->scope, source_instruction->source_node);
|
|
instruction->base.value.type = ty;
|
|
instruction->operand = operand;
|
|
|
|
ir_ref_instruction(operand, ira->new_irb.current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_widen_or_shorten(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *target)
|
|
{
|
|
IrInstructionWidenOrShorten *instruction = ir_build_instruction<IrInstructionWidenOrShorten>(
|
|
irb, scope, source_node);
|
|
instruction->target = target;
|
|
|
|
ir_ref_instruction(target, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_int_to_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *dest_type, IrInstruction *target)
|
|
{
|
|
IrInstructionIntToPtr *instruction = ir_build_instruction<IrInstructionIntToPtr>(
|
|
irb, scope, source_node);
|
|
instruction->dest_type = dest_type;
|
|
instruction->target = target;
|
|
|
|
if (dest_type) ir_ref_instruction(dest_type, irb->current_basic_block);
|
|
ir_ref_instruction(target, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_ptr_to_int(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *target)
|
|
{
|
|
IrInstructionPtrToInt *instruction = ir_build_instruction<IrInstructionPtrToInt>(
|
|
irb, scope, source_node);
|
|
instruction->target = target;
|
|
|
|
ir_ref_instruction(target, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_int_to_enum(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *dest_type, IrInstruction *target)
|
|
{
|
|
IrInstructionIntToEnum *instruction = ir_build_instruction<IrInstructionIntToEnum>(
|
|
irb, scope, source_node);
|
|
instruction->dest_type = dest_type;
|
|
instruction->target = target;
|
|
|
|
if (dest_type) ir_ref_instruction(dest_type, irb->current_basic_block);
|
|
ir_ref_instruction(target, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
|
|
|
|
static IrInstruction *ir_build_enum_to_int(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *target)
|
|
{
|
|
IrInstructionEnumToInt *instruction = ir_build_instruction<IrInstructionEnumToInt>(
|
|
irb, scope, source_node);
|
|
instruction->target = target;
|
|
|
|
ir_ref_instruction(target, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_int_to_err(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *target)
|
|
{
|
|
IrInstructionIntToErr *instruction = ir_build_instruction<IrInstructionIntToErr>(
|
|
irb, scope, source_node);
|
|
instruction->target = target;
|
|
|
|
ir_ref_instruction(target, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_err_to_int(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *target)
|
|
{
|
|
IrInstructionErrToInt *instruction = ir_build_instruction<IrInstructionErrToInt>(
|
|
irb, scope, source_node);
|
|
instruction->target = target;
|
|
|
|
ir_ref_instruction(target, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_check_switch_prongs(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *target_value, IrInstructionCheckSwitchProngsRange *ranges, size_t range_count,
|
|
bool have_else_prong)
|
|
{
|
|
IrInstructionCheckSwitchProngs *instruction = ir_build_instruction<IrInstructionCheckSwitchProngs>(
|
|
irb, scope, source_node);
|
|
instruction->target_value = target_value;
|
|
instruction->ranges = ranges;
|
|
instruction->range_count = range_count;
|
|
instruction->have_else_prong = have_else_prong;
|
|
|
|
ir_ref_instruction(target_value, irb->current_basic_block);
|
|
for (size_t i = 0; i < range_count; i += 1) {
|
|
ir_ref_instruction(ranges[i].start, irb->current_basic_block);
|
|
ir_ref_instruction(ranges[i].end, irb->current_basic_block);
|
|
}
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_check_statement_is_void(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction* statement_value)
|
|
{
|
|
IrInstructionCheckStatementIsVoid *instruction = ir_build_instruction<IrInstructionCheckStatementIsVoid>(
|
|
irb, scope, source_node);
|
|
instruction->statement_value = statement_value;
|
|
|
|
ir_ref_instruction(statement_value, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_type_name(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *type_value)
|
|
{
|
|
IrInstructionTypeName *instruction = ir_build_instruction<IrInstructionTypeName>(
|
|
irb, scope, source_node);
|
|
instruction->type_value = type_value;
|
|
|
|
ir_ref_instruction(type_value, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_decl_ref(IrBuilder *irb, Scope *scope, AstNode *source_node, Tld *tld, LVal lval) {
|
|
IrInstructionDeclRef *instruction = ir_build_instruction<IrInstructionDeclRef>(irb, scope, source_node);
|
|
instruction->tld = tld;
|
|
instruction->lval = lval;
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_panic(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *msg) {
|
|
IrInstructionPanic *instruction = ir_build_instruction<IrInstructionPanic>(irb, scope, source_node);
|
|
instruction->base.value.special = ConstValSpecialStatic;
|
|
instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable;
|
|
instruction->msg = msg;
|
|
|
|
ir_ref_instruction(msg, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_tag_name(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *target)
|
|
{
|
|
IrInstructionTagName *instruction = ir_build_instruction<IrInstructionTagName>(irb, scope, source_node);
|
|
instruction->target = target;
|
|
|
|
ir_ref_instruction(target, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_tag_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *target)
|
|
{
|
|
IrInstructionTagType *instruction = ir_build_instruction<IrInstructionTagType>(irb, scope, source_node);
|
|
instruction->target = target;
|
|
|
|
ir_ref_instruction(target, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_field_parent_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *type_value, IrInstruction *field_name, IrInstruction *field_ptr, TypeStructField *field)
|
|
{
|
|
IrInstructionFieldParentPtr *instruction = ir_build_instruction<IrInstructionFieldParentPtr>(
|
|
irb, scope, source_node);
|
|
instruction->type_value = type_value;
|
|
instruction->field_name = field_name;
|
|
instruction->field_ptr = field_ptr;
|
|
instruction->field = field;
|
|
|
|
ir_ref_instruction(type_value, irb->current_basic_block);
|
|
ir_ref_instruction(field_name, irb->current_basic_block);
|
|
ir_ref_instruction(field_ptr, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_byte_offset_of(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *type_value, IrInstruction *field_name)
|
|
{
|
|
IrInstructionByteOffsetOf *instruction = ir_build_instruction<IrInstructionByteOffsetOf>(irb, scope, source_node);
|
|
instruction->type_value = type_value;
|
|
instruction->field_name = field_name;
|
|
|
|
ir_ref_instruction(type_value, irb->current_basic_block);
|
|
ir_ref_instruction(field_name, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_bit_offset_of(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *type_value, IrInstruction *field_name)
|
|
{
|
|
IrInstructionBitOffsetOf *instruction = ir_build_instruction<IrInstructionBitOffsetOf>(irb, scope, source_node);
|
|
instruction->type_value = type_value;
|
|
instruction->field_name = field_name;
|
|
|
|
ir_ref_instruction(type_value, irb->current_basic_block);
|
|
ir_ref_instruction(field_name, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_type_info(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *type_value) {
|
|
IrInstructionTypeInfo *instruction = ir_build_instruction<IrInstructionTypeInfo>(irb, scope, source_node);
|
|
instruction->type_value = type_value;
|
|
|
|
ir_ref_instruction(type_value, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_type_id(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *type_value)
|
|
{
|
|
IrInstructionTypeId *instruction = ir_build_instruction<IrInstructionTypeId>(irb, scope, source_node);
|
|
instruction->type_value = type_value;
|
|
|
|
ir_ref_instruction(type_value, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_set_eval_branch_quota(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *new_quota)
|
|
{
|
|
IrInstructionSetEvalBranchQuota *instruction = ir_build_instruction<IrInstructionSetEvalBranchQuota>(irb, scope, source_node);
|
|
instruction->new_quota = new_quota;
|
|
|
|
ir_ref_instruction(new_quota, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_align_cast(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *align_bytes, IrInstruction *target)
|
|
{
|
|
IrInstructionAlignCast *instruction = ir_build_instruction<IrInstructionAlignCast>(irb, scope, source_node);
|
|
instruction->align_bytes = align_bytes;
|
|
instruction->target = target;
|
|
|
|
if (align_bytes) ir_ref_instruction(align_bytes, irb->current_basic_block);
|
|
ir_ref_instruction(target, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_implicit_cast(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *dest_type, IrInstruction *target, ResultLoc *result_loc)
|
|
{
|
|
IrInstructionImplicitCast *instruction = ir_build_instruction<IrInstructionImplicitCast>(irb, scope, source_node);
|
|
instruction->dest_type = dest_type;
|
|
instruction->target = target;
|
|
instruction->result_loc = result_loc;
|
|
|
|
ir_ref_instruction(dest_type, irb->current_basic_block);
|
|
ir_ref_instruction(target, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_resolve_result(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
ResultLoc *result_loc, IrInstruction *ty)
|
|
{
|
|
IrInstructionResolveResult *instruction = ir_build_instruction<IrInstructionResolveResult>(irb, scope, source_node);
|
|
instruction->result_loc = result_loc;
|
|
instruction->ty = ty;
|
|
|
|
ir_ref_instruction(ty, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_reset_result(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
ResultLoc *result_loc)
|
|
{
|
|
IrInstructionResetResult *instruction = ir_build_instruction<IrInstructionResetResult>(irb, scope, source_node);
|
|
instruction->result_loc = result_loc;
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_opaque_type(IrBuilder *irb, Scope *scope, AstNode *source_node) {
|
|
IrInstructionOpaqueType *instruction = ir_build_instruction<IrInstructionOpaqueType>(irb, scope, source_node);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_set_align_stack(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *align_bytes)
|
|
{
|
|
IrInstructionSetAlignStack *instruction = ir_build_instruction<IrInstructionSetAlignStack>(irb, scope, source_node);
|
|
instruction->align_bytes = align_bytes;
|
|
|
|
ir_ref_instruction(align_bytes, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_arg_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *fn_type, IrInstruction *arg_index)
|
|
{
|
|
IrInstructionArgType *instruction = ir_build_instruction<IrInstructionArgType>(irb, scope, source_node);
|
|
instruction->fn_type = fn_type;
|
|
instruction->arg_index = arg_index;
|
|
|
|
ir_ref_instruction(fn_type, irb->current_basic_block);
|
|
ir_ref_instruction(arg_index, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_error_return_trace(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstructionErrorReturnTrace::Optional optional) {
|
|
IrInstructionErrorReturnTrace *instruction = ir_build_instruction<IrInstructionErrorReturnTrace>(irb, scope, source_node);
|
|
instruction->optional = optional;
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_error_union(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *err_set, IrInstruction *payload)
|
|
{
|
|
IrInstructionErrorUnion *instruction = ir_build_instruction<IrInstructionErrorUnion>(irb, scope, source_node);
|
|
instruction->err_set = err_set;
|
|
instruction->payload = payload;
|
|
|
|
ir_ref_instruction(err_set, irb->current_basic_block);
|
|
ir_ref_instruction(payload, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_atomic_rmw(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *operand_type, IrInstruction *ptr, IrInstruction *op, IrInstruction *operand,
|
|
IrInstruction *ordering, AtomicRmwOp resolved_op, AtomicOrder resolved_ordering)
|
|
{
|
|
IrInstructionAtomicRmw *instruction = ir_build_instruction<IrInstructionAtomicRmw>(irb, scope, source_node);
|
|
instruction->operand_type = operand_type;
|
|
instruction->ptr = ptr;
|
|
instruction->op = op;
|
|
instruction->operand = operand;
|
|
instruction->ordering = ordering;
|
|
instruction->resolved_op = resolved_op;
|
|
instruction->resolved_ordering = resolved_ordering;
|
|
|
|
if (operand_type != nullptr) ir_ref_instruction(operand_type, irb->current_basic_block);
|
|
ir_ref_instruction(ptr, irb->current_basic_block);
|
|
if (op != nullptr) ir_ref_instruction(op, irb->current_basic_block);
|
|
ir_ref_instruction(operand, irb->current_basic_block);
|
|
if (ordering != nullptr) ir_ref_instruction(ordering, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_atomic_load(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *operand_type, IrInstruction *ptr,
|
|
IrInstruction *ordering, AtomicOrder resolved_ordering)
|
|
{
|
|
IrInstructionAtomicLoad *instruction = ir_build_instruction<IrInstructionAtomicLoad>(irb, scope, source_node);
|
|
instruction->operand_type = operand_type;
|
|
instruction->ptr = ptr;
|
|
instruction->ordering = ordering;
|
|
instruction->resolved_ordering = resolved_ordering;
|
|
|
|
if (operand_type != nullptr) ir_ref_instruction(operand_type, irb->current_basic_block);
|
|
ir_ref_instruction(ptr, irb->current_basic_block);
|
|
if (ordering != nullptr) ir_ref_instruction(ordering, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_save_err_ret_addr(IrBuilder *irb, Scope *scope, AstNode *source_node) {
|
|
IrInstructionSaveErrRetAddr *instruction = ir_build_instruction<IrInstructionSaveErrRetAddr>(irb, scope, source_node);
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_add_implicit_return_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *value)
|
|
{
|
|
IrInstructionAddImplicitReturnType *instruction = ir_build_instruction<IrInstructionAddImplicitReturnType>(irb, scope, source_node);
|
|
instruction->value = value;
|
|
|
|
ir_ref_instruction(value, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_has_decl(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *container, IrInstruction *name)
|
|
{
|
|
IrInstructionHasDecl *instruction = ir_build_instruction<IrInstructionHasDecl>(irb, scope, source_node);
|
|
instruction->container = container;
|
|
instruction->name = name;
|
|
|
|
ir_ref_instruction(container, irb->current_basic_block);
|
|
ir_ref_instruction(name, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_undeclared_identifier(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
Buf *name)
|
|
{
|
|
IrInstructionUndeclaredIdent *instruction = ir_build_instruction<IrInstructionUndeclaredIdent>(irb, scope, source_node);
|
|
instruction->name = name;
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_check_runtime_scope(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *scope_is_comptime, IrInstruction *is_comptime) {
|
|
IrInstructionCheckRuntimeScope *instruction = ir_build_instruction<IrInstructionCheckRuntimeScope>(irb, scope, source_node);
|
|
instruction->scope_is_comptime = scope_is_comptime;
|
|
instruction->is_comptime = is_comptime;
|
|
|
|
ir_ref_instruction(scope_is_comptime, irb->current_basic_block);
|
|
ir_ref_instruction(is_comptime, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_union_init_named_field(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *union_type, IrInstruction *field_name, IrInstruction *field_result_loc, IrInstruction *result_loc)
|
|
{
|
|
IrInstructionUnionInitNamedField *instruction = ir_build_instruction<IrInstructionUnionInitNamedField>(irb, scope, source_node);
|
|
instruction->union_type = union_type;
|
|
instruction->field_name = field_name;
|
|
instruction->field_result_loc = field_result_loc;
|
|
instruction->result_loc = result_loc;
|
|
|
|
ir_ref_instruction(union_type, irb->current_basic_block);
|
|
ir_ref_instruction(field_name, irb->current_basic_block);
|
|
ir_ref_instruction(field_result_loc, irb->current_basic_block);
|
|
if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
|
|
static IrInstruction *ir_build_vector_to_array(IrAnalyze *ira, IrInstruction *source_instruction,
|
|
ZigType *result_type, IrInstruction *vector, IrInstruction *result_loc)
|
|
{
|
|
IrInstructionVectorToArray *instruction = ir_build_instruction<IrInstructionVectorToArray>(&ira->new_irb,
|
|
source_instruction->scope, source_instruction->source_node);
|
|
instruction->base.value.type = result_type;
|
|
instruction->vector = vector;
|
|
instruction->result_loc = result_loc;
|
|
|
|
ir_ref_instruction(vector, ira->new_irb.current_basic_block);
|
|
ir_ref_instruction(result_loc, ira->new_irb.current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_ptr_of_array_to_slice(IrAnalyze *ira, IrInstruction *source_instruction,
|
|
ZigType *result_type, IrInstruction *operand, IrInstruction *result_loc)
|
|
{
|
|
IrInstructionPtrOfArrayToSlice *instruction = ir_build_instruction<IrInstructionPtrOfArrayToSlice>(&ira->new_irb,
|
|
source_instruction->scope, source_instruction->source_node);
|
|
instruction->base.value.type = result_type;
|
|
instruction->operand = operand;
|
|
instruction->result_loc = result_loc;
|
|
|
|
ir_ref_instruction(operand, ira->new_irb.current_basic_block);
|
|
ir_ref_instruction(result_loc, ira->new_irb.current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_array_to_vector(IrAnalyze *ira, IrInstruction *source_instruction,
|
|
IrInstruction *array, ZigType *result_type)
|
|
{
|
|
IrInstructionArrayToVector *instruction = ir_build_instruction<IrInstructionArrayToVector>(&ira->new_irb,
|
|
source_instruction->scope, source_instruction->source_node);
|
|
instruction->base.value.type = result_type;
|
|
instruction->array = array;
|
|
|
|
ir_ref_instruction(array, ira->new_irb.current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_assert_zero(IrAnalyze *ira, IrInstruction *source_instruction,
|
|
IrInstruction *target)
|
|
{
|
|
IrInstructionAssertZero *instruction = ir_build_instruction<IrInstructionAssertZero>(&ira->new_irb,
|
|
source_instruction->scope, source_instruction->source_node);
|
|
instruction->base.value.type = ira->codegen->builtin_types.entry_void;
|
|
instruction->target = target;
|
|
|
|
ir_ref_instruction(target, ira->new_irb.current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_assert_non_null(IrAnalyze *ira, IrInstruction *source_instruction,
|
|
IrInstruction *target)
|
|
{
|
|
IrInstructionAssertNonNull *instruction = ir_build_instruction<IrInstructionAssertNonNull>(&ira->new_irb,
|
|
source_instruction->scope, source_instruction->source_node);
|
|
instruction->base.value.type = ira->codegen->builtin_types.entry_void;
|
|
instruction->target = target;
|
|
|
|
ir_ref_instruction(target, ira->new_irb.current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_alloca_src(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *align, const char *name_hint, IrInstruction *is_comptime)
|
|
{
|
|
IrInstructionAllocaSrc *instruction = ir_build_instruction<IrInstructionAllocaSrc>(irb, scope, source_node);
|
|
instruction->base.is_gen = true;
|
|
instruction->align = align;
|
|
instruction->name_hint = name_hint;
|
|
instruction->is_comptime = is_comptime;
|
|
|
|
if (align != nullptr) ir_ref_instruction(align, irb->current_basic_block);
|
|
if (is_comptime != nullptr) ir_ref_instruction(is_comptime, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstructionAllocaGen *ir_build_alloca_gen(IrAnalyze *ira, IrInstruction *source_instruction,
|
|
uint32_t align, const char *name_hint)
|
|
{
|
|
IrInstructionAllocaGen *instruction = ir_create_instruction<IrInstructionAllocaGen>(&ira->new_irb,
|
|
source_instruction->scope, source_instruction->source_node);
|
|
instruction->align = align;
|
|
instruction->name_hint = name_hint;
|
|
|
|
return instruction;
|
|
}
|
|
|
|
static IrInstruction *ir_build_end_expr(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *value, ResultLoc *result_loc)
|
|
{
|
|
IrInstructionEndExpr *instruction = ir_build_instruction<IrInstructionEndExpr>(irb, scope, source_node);
|
|
instruction->base.is_gen = true;
|
|
instruction->value = value;
|
|
instruction->result_loc = result_loc;
|
|
|
|
ir_ref_instruction(value, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstructionSuspendBegin *ir_build_suspend_begin(IrBuilder *irb, Scope *scope, AstNode *source_node) {
|
|
IrInstructionSuspendBegin *instruction = ir_build_instruction<IrInstructionSuspendBegin>(irb, scope, source_node);
|
|
instruction->base.value.type = irb->codegen->builtin_types.entry_void;
|
|
|
|
return instruction;
|
|
}
|
|
|
|
static IrInstruction *ir_build_suspend_finish(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstructionSuspendBegin *begin)
|
|
{
|
|
IrInstructionSuspendFinish *instruction = ir_build_instruction<IrInstructionSuspendFinish>(irb, scope, source_node);
|
|
instruction->base.value.type = irb->codegen->builtin_types.entry_void;
|
|
instruction->begin = begin;
|
|
|
|
ir_ref_instruction(&begin->base, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_await_src(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *frame, ResultLoc *result_loc)
|
|
{
|
|
IrInstructionAwaitSrc *instruction = ir_build_instruction<IrInstructionAwaitSrc>(irb, scope, source_node);
|
|
instruction->frame = frame;
|
|
instruction->result_loc = result_loc;
|
|
|
|
ir_ref_instruction(frame, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_await_gen(IrAnalyze *ira, IrInstruction *source_instruction,
|
|
IrInstruction *frame, ZigType *result_type, IrInstruction *result_loc)
|
|
{
|
|
IrInstructionAwaitGen *instruction = ir_build_instruction<IrInstructionAwaitGen>(&ira->new_irb,
|
|
source_instruction->scope, source_instruction->source_node);
|
|
instruction->base.value.type = result_type;
|
|
instruction->frame = frame;
|
|
instruction->result_loc = result_loc;
|
|
|
|
ir_ref_instruction(frame, ira->new_irb.current_basic_block);
|
|
if (result_loc != nullptr) ir_ref_instruction(result_loc, ira->new_irb.current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_coro_resume(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *frame)
|
|
{
|
|
IrInstructionCoroResume *instruction = ir_build_instruction<IrInstructionCoroResume>(irb, scope, source_node);
|
|
instruction->base.value.type = irb->codegen->builtin_types.entry_void;
|
|
instruction->frame = frame;
|
|
|
|
ir_ref_instruction(frame, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
|
|
results[ReturnKindUnconditional] = 0;
|
|
results[ReturnKindError] = 0;
|
|
|
|
Scope *scope = inner_scope;
|
|
|
|
while (scope != outer_scope) {
|
|
assert(scope);
|
|
switch (scope->id) {
|
|
case ScopeIdDefer: {
|
|
AstNode *defer_node = scope->source_node;
|
|
assert(defer_node->type == NodeTypeDefer);
|
|
ReturnKind defer_kind = defer_node->data.defer.kind;
|
|
results[defer_kind] += 1;
|
|
scope = scope->parent;
|
|
continue;
|
|
}
|
|
case ScopeIdDecls:
|
|
case ScopeIdFnDef:
|
|
return;
|
|
case ScopeIdBlock:
|
|
case ScopeIdVarDecl:
|
|
case ScopeIdLoop:
|
|
case ScopeIdSuspend:
|
|
case ScopeIdCompTime:
|
|
case ScopeIdRuntime:
|
|
scope = scope->parent;
|
|
continue;
|
|
case ScopeIdDeferExpr:
|
|
case ScopeIdCImport:
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
}
|
|
|
|
static IrInstruction *ir_mark_gen(IrInstruction *instruction) {
|
|
instruction->is_gen = true;
|
|
return instruction;
|
|
}
|
|
|
|
static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, bool gen_error_defers) {
|
|
Scope *scope = inner_scope;
|
|
bool is_noreturn = false;
|
|
while (scope != outer_scope) {
|
|
if (!scope)
|
|
return is_noreturn;
|
|
|
|
switch (scope->id) {
|
|
case ScopeIdDefer: {
|
|
AstNode *defer_node = scope->source_node;
|
|
assert(defer_node->type == NodeTypeDefer);
|
|
ReturnKind defer_kind = defer_node->data.defer.kind;
|
|
if (defer_kind == ReturnKindUnconditional ||
|
|
(gen_error_defers && defer_kind == ReturnKindError))
|
|
{
|
|
AstNode *defer_expr_node = defer_node->data.defer.expr;
|
|
Scope *defer_expr_scope = defer_node->data.defer.expr_scope;
|
|
IrInstruction *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope);
|
|
if (defer_expr_value != irb->codegen->invalid_instruction) {
|
|
if (defer_expr_value->value.type != nullptr &&
|
|
defer_expr_value->value.type->id == ZigTypeIdUnreachable)
|
|
{
|
|
is_noreturn = true;
|
|
} else {
|
|
ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node,
|
|
defer_expr_value));
|
|
}
|
|
}
|
|
}
|
|
scope = scope->parent;
|
|
continue;
|
|
}
|
|
case ScopeIdDecls:
|
|
case ScopeIdFnDef:
|
|
return is_noreturn;
|
|
case ScopeIdBlock:
|
|
case ScopeIdVarDecl:
|
|
case ScopeIdLoop:
|
|
case ScopeIdSuspend:
|
|
case ScopeIdCompTime:
|
|
case ScopeIdRuntime:
|
|
scope = scope->parent;
|
|
continue;
|
|
case ScopeIdDeferExpr:
|
|
case ScopeIdCImport:
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
return is_noreturn;
|
|
}
|
|
|
|
static void ir_set_cursor_at_end(IrBuilder *irb, IrBasicBlock *basic_block) {
|
|
assert(basic_block);
|
|
|
|
irb->current_basic_block = basic_block;
|
|
}
|
|
|
|
static void ir_set_cursor_at_end_and_append_block(IrBuilder *irb, IrBasicBlock *basic_block) {
|
|
basic_block->index = irb->exec->basic_block_list.length;
|
|
irb->exec->basic_block_list.append(basic_block);
|
|
ir_set_cursor_at_end(irb, basic_block);
|
|
}
|
|
|
|
static ScopeSuspend *get_scope_suspend(Scope *scope) {
|
|
while (scope) {
|
|
if (scope->id == ScopeIdSuspend)
|
|
return (ScopeSuspend *)scope;
|
|
if (scope->id == ScopeIdFnDef)
|
|
return nullptr;
|
|
|
|
scope = scope->parent;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
static ScopeDeferExpr *get_scope_defer_expr(Scope *scope) {
|
|
while (scope) {
|
|
if (scope->id == ScopeIdDeferExpr)
|
|
return (ScopeDeferExpr *)scope;
|
|
if (scope->id == ScopeIdFnDef)
|
|
return nullptr;
|
|
|
|
scope = scope->parent;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) {
|
|
assert(node->type == NodeTypeReturnExpr);
|
|
|
|
ZigFn *fn_entry = exec_fn_entry(irb->exec);
|
|
if (!fn_entry) {
|
|
add_node_error(irb->codegen, node, buf_sprintf("return expression outside function definition"));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
|
|
ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(scope);
|
|
if (scope_defer_expr) {
|
|
if (!scope_defer_expr->reported_err) {
|
|
add_node_error(irb->codegen, node, buf_sprintf("cannot return from defer expression"));
|
|
scope_defer_expr->reported_err = true;
|
|
}
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
|
|
Scope *outer_scope = irb->exec->begin_scope;
|
|
|
|
AstNode *expr_node = node->data.return_expr.expr;
|
|
switch (node->data.return_expr.kind) {
|
|
case ReturnKindUnconditional:
|
|
{
|
|
ResultLocReturn *result_loc_ret = allocate<ResultLocReturn>(1);
|
|
result_loc_ret->base.id = ResultLocIdReturn;
|
|
ir_build_reset_result(irb, scope, node, &result_loc_ret->base);
|
|
|
|
IrInstruction *return_value;
|
|
if (expr_node) {
|
|
// Temporarily set this so that if we return a type it gets the name of the function
|
|
ZigFn *prev_name_fn = irb->exec->name_fn;
|
|
irb->exec->name_fn = exec_fn_entry(irb->exec);
|
|
return_value = ir_gen_node_extra(irb, expr_node, scope, LValNone, &result_loc_ret->base);
|
|
irb->exec->name_fn = prev_name_fn;
|
|
if (return_value == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
} else {
|
|
return_value = ir_build_const_void(irb, scope, node);
|
|
}
|
|
|
|
ir_build_return_begin(irb, scope, node, return_value);
|
|
|
|
size_t defer_counts[2];
|
|
ir_count_defers(irb, scope, outer_scope, defer_counts);
|
|
bool have_err_defers = defer_counts[ReturnKindError] > 0;
|
|
if (have_err_defers || irb->codegen->have_err_ret_tracing) {
|
|
IrBasicBlock *err_block = ir_create_basic_block(irb, scope, "ErrRetErr");
|
|
IrBasicBlock *ok_block = ir_create_basic_block(irb, scope, "ErrRetOk");
|
|
|
|
IrInstruction *is_err = ir_build_test_err_src(irb, scope, node, return_value, false, true);
|
|
|
|
bool should_inline = ir_should_inline(irb->exec, scope);
|
|
IrInstruction *is_comptime;
|
|
if (should_inline) {
|
|
is_comptime = ir_build_const_bool(irb, scope, node, true);
|
|
} else {
|
|
is_comptime = ir_build_test_comptime(irb, scope, node, is_err);
|
|
}
|
|
|
|
ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err, err_block, ok_block, is_comptime));
|
|
IrBasicBlock *ret_stmt_block = ir_create_basic_block(irb, scope, "RetStmt");
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, err_block);
|
|
if (irb->codegen->have_err_ret_tracing && !should_inline) {
|
|
ir_build_save_err_ret_addr(irb, scope, node);
|
|
}
|
|
ir_gen_defers_for_block(irb, scope, outer_scope, true);
|
|
ir_build_br(irb, scope, node, ret_stmt_block, is_comptime);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, ok_block);
|
|
ir_gen_defers_for_block(irb, scope, outer_scope, false);
|
|
ir_build_br(irb, scope, node, ret_stmt_block, is_comptime);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, ret_stmt_block);
|
|
ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, return_value));
|
|
IrInstruction *result = ir_build_return(irb, scope, node, return_value);
|
|
result_loc_ret->base.source_instruction = result;
|
|
return result;
|
|
} else {
|
|
// generate unconditional defers
|
|
ir_gen_defers_for_block(irb, scope, outer_scope, false);
|
|
ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, return_value));
|
|
IrInstruction *result = ir_build_return(irb, scope, node, return_value);
|
|
result_loc_ret->base.source_instruction = result;
|
|
return result;
|
|
}
|
|
}
|
|
case ReturnKindError:
|
|
{
|
|
assert(expr_node);
|
|
IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr);
|
|
if (err_union_ptr == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
IrInstruction *is_err_val = ir_build_test_err_src(irb, scope, node, err_union_ptr, true, false);
|
|
|
|
IrBasicBlock *return_block = ir_create_basic_block(irb, scope, "ErrRetReturn");
|
|
IrBasicBlock *continue_block = ir_create_basic_block(irb, scope, "ErrRetContinue");
|
|
IrInstruction *is_comptime;
|
|
bool should_inline = ir_should_inline(irb->exec, scope);
|
|
if (should_inline) {
|
|
is_comptime = ir_build_const_bool(irb, scope, node, true);
|
|
} else {
|
|
is_comptime = ir_build_test_comptime(irb, scope, node, is_err_val);
|
|
}
|
|
ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err_val, return_block, continue_block, is_comptime));
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, return_block);
|
|
IrInstruction *err_val_ptr = ir_build_unwrap_err_code(irb, scope, node, err_union_ptr);
|
|
IrInstruction *err_val = ir_build_load_ptr(irb, scope, node, err_val_ptr);
|
|
ir_build_return_begin(irb, scope, node, err_val);
|
|
if (!ir_gen_defers_for_block(irb, scope, outer_scope, true)) {
|
|
ResultLocReturn *result_loc_ret = allocate<ResultLocReturn>(1);
|
|
result_loc_ret->base.id = ResultLocIdReturn;
|
|
ir_build_reset_result(irb, scope, node, &result_loc_ret->base);
|
|
ir_build_end_expr(irb, scope, node, err_val, &result_loc_ret->base);
|
|
|
|
if (irb->codegen->have_err_ret_tracing && !should_inline) {
|
|
ir_build_save_err_ret_addr(irb, scope, node);
|
|
}
|
|
ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, err_val));
|
|
IrInstruction *ret_inst = ir_build_return(irb, scope, node, err_val);
|
|
result_loc_ret->base.source_instruction = ret_inst;
|
|
}
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, continue_block);
|
|
IrInstruction *unwrapped_ptr = ir_build_unwrap_err_payload(irb, scope, node, err_union_ptr, false, false);
|
|
if (lval == LValPtr)
|
|
return unwrapped_ptr;
|
|
else
|
|
return ir_expr_wrap(irb, scope, ir_build_load_ptr(irb, scope, node, unwrapped_ptr), result_loc);
|
|
}
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
static ZigVar *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_scope,
|
|
Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstruction *is_comptime,
|
|
bool skip_name_check)
|
|
{
|
|
ZigVar *variable_entry = allocate<ZigVar>(1);
|
|
variable_entry->parent_scope = parent_scope;
|
|
variable_entry->shadowable = is_shadowable;
|
|
variable_entry->mem_slot_index = SIZE_MAX;
|
|
variable_entry->is_comptime = is_comptime;
|
|
variable_entry->src_arg_index = SIZE_MAX;
|
|
variable_entry->const_value = create_const_vals(1);
|
|
|
|
if (is_comptime != nullptr) {
|
|
is_comptime->ref_count += 1;
|
|
}
|
|
|
|
if (name) {
|
|
buf_init_from_buf(&variable_entry->name, name);
|
|
|
|
if (!skip_name_check) {
|
|
ZigVar *existing_var = find_variable(codegen, parent_scope, name, nullptr);
|
|
if (existing_var && !existing_var->shadowable) {
|
|
if (existing_var->var_type == nullptr || !type_is_invalid(existing_var->var_type)) {
|
|
ErrorMsg *msg = add_node_error(codegen, node,
|
|
buf_sprintf("redeclaration of variable '%s'", buf_ptr(name)));
|
|
add_error_note(codegen, msg, existing_var->decl_node, buf_sprintf("previous declaration is here"));
|
|
}
|
|
variable_entry->var_type = codegen->builtin_types.entry_invalid;
|
|
} else {
|
|
ZigType *type;
|
|
if (get_primitive_type(codegen, name, &type) != ErrorPrimitiveTypeNotFound) {
|
|
add_node_error(codegen, node,
|
|
buf_sprintf("variable shadows primitive type '%s'", buf_ptr(name)));
|
|
variable_entry->var_type = codegen->builtin_types.entry_invalid;
|
|
} else {
|
|
Tld *tld = find_decl(codegen, parent_scope, name);
|
|
if (tld != nullptr) {
|
|
ErrorMsg *msg = add_node_error(codegen, node,
|
|
buf_sprintf("redefinition of '%s'", buf_ptr(name)));
|
|
add_error_note(codegen, msg, tld->source_node, buf_sprintf("previous definition is here"));
|
|
variable_entry->var_type = codegen->builtin_types.entry_invalid;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
assert(is_shadowable);
|
|
// TODO make this name not actually be in scope. user should be able to make a variable called "_anon"
|
|
// might already be solved, let's just make sure it has test coverage
|
|
// maybe we put a prefix on this so the debug info doesn't clobber user debug info for same named variables
|
|
buf_init_from_str(&variable_entry->name, "_anon");
|
|
}
|
|
|
|
variable_entry->src_is_const = src_is_const;
|
|
variable_entry->gen_is_const = gen_is_const;
|
|
variable_entry->decl_node = node;
|
|
variable_entry->child_scope = create_var_scope(codegen, node, parent_scope, variable_entry);
|
|
|
|
return variable_entry;
|
|
}
|
|
|
|
// Set name to nullptr to make the variable anonymous (not visible to programmer).
|
|
// After you call this function var->child_scope has the variable in scope
|
|
static ZigVar *ir_create_var(IrBuilder *irb, AstNode *node, Scope *scope, Buf *name,
|
|
bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstruction *is_comptime)
|
|
{
|
|
bool is_underscored = name ? buf_eql_str(name, "_") : false;
|
|
ZigVar *var = create_local_var(irb->codegen, node, scope,
|
|
(is_underscored ? nullptr : name), src_is_const, gen_is_const,
|
|
(is_underscored ? true : is_shadowable), is_comptime, false);
|
|
if (is_comptime != nullptr || gen_is_const) {
|
|
var->mem_slot_index = exec_next_mem_slot(irb->exec);
|
|
var->owner_exec = irb->exec;
|
|
}
|
|
assert(var->child_scope);
|
|
return var;
|
|
}
|
|
|
|
static ResultLocPeer *create_peer_result(ResultLocPeerParent *peer_parent) {
|
|
ResultLocPeer *result = allocate<ResultLocPeer>(1);
|
|
result->base.id = ResultLocIdPeer;
|
|
result->base.source_instruction = peer_parent->base.source_instruction;
|
|
result->parent = peer_parent;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode *block_node, LVal lval,
|
|
ResultLoc *result_loc)
|
|
{
|
|
assert(block_node->type == NodeTypeBlock);
|
|
|
|
ZigList<IrInstruction *> incoming_values = {0};
|
|
ZigList<IrBasicBlock *> incoming_blocks = {0};
|
|
|
|
ScopeBlock *scope_block = create_block_scope(irb->codegen, block_node, parent_scope);
|
|
|
|
Scope *outer_block_scope = &scope_block->base;
|
|
Scope *child_scope = outer_block_scope;
|
|
|
|
ZigFn *fn_entry = scope_fn_entry(parent_scope);
|
|
if (fn_entry && fn_entry->child_scope == parent_scope) {
|
|
fn_entry->def_scope = scope_block;
|
|
}
|
|
|
|
if (block_node->data.block.statements.length == 0) {
|
|
// {}
|
|
return ir_lval_wrap(irb, parent_scope, ir_build_const_void(irb, child_scope, block_node), lval, result_loc);
|
|
}
|
|
|
|
if (block_node->data.block.name != nullptr) {
|
|
scope_block->lval = lval;
|
|
scope_block->incoming_blocks = &incoming_blocks;
|
|
scope_block->incoming_values = &incoming_values;
|
|
scope_block->end_block = ir_create_basic_block(irb, parent_scope, "BlockEnd");
|
|
scope_block->is_comptime = ir_build_const_bool(irb, parent_scope, block_node,
|
|
ir_should_inline(irb->exec, parent_scope));
|
|
|
|
scope_block->peer_parent = allocate<ResultLocPeerParent>(1);
|
|
scope_block->peer_parent->base.id = ResultLocIdPeerParent;
|
|
scope_block->peer_parent->base.source_instruction = scope_block->is_comptime;
|
|
scope_block->peer_parent->end_bb = scope_block->end_block;
|
|
scope_block->peer_parent->is_comptime = scope_block->is_comptime;
|
|
scope_block->peer_parent->parent = result_loc;
|
|
ir_build_reset_result(irb, parent_scope, block_node, &scope_block->peer_parent->base);
|
|
}
|
|
|
|
bool is_continuation_unreachable = false;
|
|
IrInstruction *noreturn_return_value = nullptr;
|
|
for (size_t i = 0; i < block_node->data.block.statements.length; i += 1) {
|
|
AstNode *statement_node = block_node->data.block.statements.at(i);
|
|
|
|
IrInstruction *statement_value = ir_gen_node(irb, statement_node, child_scope);
|
|
is_continuation_unreachable = instr_is_unreachable(statement_value);
|
|
if (is_continuation_unreachable) {
|
|
// keep the last noreturn statement value around in case we need to return it
|
|
noreturn_return_value = statement_value;
|
|
}
|
|
// This logic must be kept in sync with
|
|
// [STMT_EXPR_TEST_THING] <--- (search this token)
|
|
if (statement_node->type == NodeTypeDefer && statement_value != irb->codegen->invalid_instruction) {
|
|
// defer starts a new scope
|
|
child_scope = statement_node->data.defer.child_scope;
|
|
assert(child_scope);
|
|
} else if (statement_value->id == IrInstructionIdDeclVarSrc) {
|
|
// variable declarations start a new scope
|
|
IrInstructionDeclVarSrc *decl_var_instruction = (IrInstructionDeclVarSrc *)statement_value;
|
|
child_scope = decl_var_instruction->var->child_scope;
|
|
} else if (statement_value != irb->codegen->invalid_instruction && !is_continuation_unreachable) {
|
|
// this statement's value must be void
|
|
ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, statement_node, statement_value));
|
|
}
|
|
}
|
|
|
|
if (is_continuation_unreachable) {
|
|
assert(noreturn_return_value != nullptr);
|
|
if (block_node->data.block.name == nullptr || incoming_blocks.length == 0) {
|
|
return noreturn_return_value;
|
|
}
|
|
|
|
if (scope_block->peer_parent != nullptr && scope_block->peer_parent->peers.length != 0) {
|
|
scope_block->peer_parent->peers.last()->next_bb = scope_block->end_block;
|
|
}
|
|
ir_set_cursor_at_end_and_append_block(irb, scope_block->end_block);
|
|
IrInstruction *phi = ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length,
|
|
incoming_blocks.items, incoming_values.items, scope_block->peer_parent);
|
|
return ir_expr_wrap(irb, parent_scope, phi, result_loc);
|
|
} else {
|
|
incoming_blocks.append(irb->current_basic_block);
|
|
IrInstruction *else_expr_result = ir_mark_gen(ir_build_const_void(irb, parent_scope, block_node));
|
|
|
|
if (scope_block->peer_parent != nullptr) {
|
|
ResultLocPeer *peer_result = create_peer_result(scope_block->peer_parent);
|
|
scope_block->peer_parent->peers.append(peer_result);
|
|
ir_build_end_expr(irb, parent_scope, block_node, else_expr_result, &peer_result->base);
|
|
|
|
if (scope_block->peer_parent->peers.length != 0) {
|
|
scope_block->peer_parent->peers.last()->next_bb = scope_block->end_block;
|
|
}
|
|
}
|
|
|
|
incoming_values.append(else_expr_result);
|
|
}
|
|
|
|
if (block_node->data.block.name != nullptr) {
|
|
ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false);
|
|
ir_mark_gen(ir_build_br(irb, parent_scope, block_node, scope_block->end_block, scope_block->is_comptime));
|
|
ir_set_cursor_at_end_and_append_block(irb, scope_block->end_block);
|
|
IrInstruction *phi = ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length,
|
|
incoming_blocks.items, incoming_values.items, scope_block->peer_parent);
|
|
return ir_expr_wrap(irb, parent_scope, phi, result_loc);
|
|
} else {
|
|
ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false);
|
|
IrInstruction *void_inst = ir_mark_gen(ir_build_const_void(irb, child_scope, block_node));
|
|
return ir_lval_wrap(irb, parent_scope, void_inst, lval, result_loc);
|
|
}
|
|
}
|
|
|
|
static IrInstruction *ir_gen_bin_op_id(IrBuilder *irb, Scope *scope, AstNode *node, IrBinOp op_id) {
|
|
Scope *inner_scope = scope;
|
|
if (op_id == IrBinOpArrayCat || op_id == IrBinOpArrayMult) {
|
|
inner_scope = create_comptime_scope(irb->codegen, node, scope);
|
|
}
|
|
|
|
IrInstruction *op1 = ir_gen_node(irb, node->data.bin_op_expr.op1, inner_scope);
|
|
IrInstruction *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, inner_scope);
|
|
|
|
if (op1 == irb->codegen->invalid_instruction || op2 == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
return ir_build_bin_op(irb, scope, node, op_id, op1, op2, true);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_assign(IrBuilder *irb, Scope *scope, AstNode *node) {
|
|
IrInstruction *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValPtr, nullptr);
|
|
if (lvalue == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
ResultLocInstruction *result_loc_inst = allocate<ResultLocInstruction>(1);
|
|
result_loc_inst->base.id = ResultLocIdInstruction;
|
|
result_loc_inst->base.source_instruction = lvalue;
|
|
ir_ref_instruction(lvalue, irb->current_basic_block);
|
|
ir_build_reset_result(irb, scope, node, &result_loc_inst->base);
|
|
|
|
IrInstruction *rvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op2, scope, LValNone,
|
|
&result_loc_inst->base);
|
|
if (rvalue == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
return ir_build_const_void(irb, scope, node);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_assign_op(IrBuilder *irb, Scope *scope, AstNode *node, IrBinOp op_id) {
|
|
IrInstruction *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValPtr, nullptr);
|
|
if (lvalue == irb->codegen->invalid_instruction)
|
|
return lvalue;
|
|
IrInstruction *op1 = ir_build_load_ptr(irb, scope, node->data.bin_op_expr.op1, lvalue);
|
|
IrInstruction *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope);
|
|
if (op2 == irb->codegen->invalid_instruction)
|
|
return op2;
|
|
IrInstruction *result = ir_build_bin_op(irb, scope, node, op_id, op1, op2, true);
|
|
ir_build_store_ptr(irb, scope, node, lvalue, result);
|
|
return ir_build_const_void(irb, scope, node);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_bool_or(IrBuilder *irb, Scope *scope, AstNode *node) {
|
|
assert(node->type == NodeTypeBinOpExpr);
|
|
|
|
IrInstruction *val1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope);
|
|
if (val1 == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
IrBasicBlock *post_val1_block = irb->current_basic_block;
|
|
|
|
IrInstruction *is_comptime;
|
|
if (ir_should_inline(irb->exec, scope)) {
|
|
is_comptime = ir_build_const_bool(irb, scope, node, true);
|
|
} else {
|
|
is_comptime = ir_build_test_comptime(irb, scope, node, val1);
|
|
}
|
|
|
|
// block for when val1 == false
|
|
IrBasicBlock *false_block = ir_create_basic_block(irb, scope, "BoolOrFalse");
|
|
// block for when val1 == true (don't even evaluate the second part)
|
|
IrBasicBlock *true_block = ir_create_basic_block(irb, scope, "BoolOrTrue");
|
|
|
|
ir_build_cond_br(irb, scope, node, val1, true_block, false_block, is_comptime);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, false_block);
|
|
IrInstruction *val2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope);
|
|
if (val2 == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
IrBasicBlock *post_val2_block = irb->current_basic_block;
|
|
|
|
ir_build_br(irb, scope, node, true_block, is_comptime);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, true_block);
|
|
|
|
IrInstruction **incoming_values = allocate<IrInstruction *>(2);
|
|
incoming_values[0] = val1;
|
|
incoming_values[1] = val2;
|
|
IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
|
|
incoming_blocks[0] = post_val1_block;
|
|
incoming_blocks[1] = post_val2_block;
|
|
|
|
return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, nullptr);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_bool_and(IrBuilder *irb, Scope *scope, AstNode *node) {
|
|
assert(node->type == NodeTypeBinOpExpr);
|
|
|
|
IrInstruction *val1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope);
|
|
if (val1 == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
IrBasicBlock *post_val1_block = irb->current_basic_block;
|
|
|
|
IrInstruction *is_comptime;
|
|
if (ir_should_inline(irb->exec, scope)) {
|
|
is_comptime = ir_build_const_bool(irb, scope, node, true);
|
|
} else {
|
|
is_comptime = ir_build_test_comptime(irb, scope, node, val1);
|
|
}
|
|
|
|
// block for when val1 == true
|
|
IrBasicBlock *true_block = ir_create_basic_block(irb, scope, "BoolAndTrue");
|
|
// block for when val1 == false (don't even evaluate the second part)
|
|
IrBasicBlock *false_block = ir_create_basic_block(irb, scope, "BoolAndFalse");
|
|
|
|
ir_build_cond_br(irb, scope, node, val1, true_block, false_block, is_comptime);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, true_block);
|
|
IrInstruction *val2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope);
|
|
if (val2 == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
IrBasicBlock *post_val2_block = irb->current_basic_block;
|
|
|
|
ir_build_br(irb, scope, node, false_block, is_comptime);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, false_block);
|
|
|
|
IrInstruction **incoming_values = allocate<IrInstruction *>(2);
|
|
incoming_values[0] = val1;
|
|
incoming_values[1] = val2;
|
|
IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
|
|
incoming_blocks[0] = post_val1_block;
|
|
incoming_blocks[1] = post_val2_block;
|
|
|
|
return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, nullptr);
|
|
}
|
|
|
|
static ResultLocPeerParent *ir_build_result_peers(IrBuilder *irb, IrInstruction *cond_br_inst,
|
|
IrBasicBlock *end_block, ResultLoc *parent, IrInstruction *is_comptime)
|
|
{
|
|
ResultLocPeerParent *peer_parent = allocate<ResultLocPeerParent>(1);
|
|
peer_parent->base.id = ResultLocIdPeerParent;
|
|
peer_parent->base.source_instruction = cond_br_inst;
|
|
peer_parent->end_bb = end_block;
|
|
peer_parent->is_comptime = is_comptime;
|
|
peer_parent->parent = parent;
|
|
|
|
IrInstruction *popped_inst = irb->current_basic_block->instruction_list.pop();
|
|
ir_assert(popped_inst == cond_br_inst, cond_br_inst);
|
|
|
|
ir_build_reset_result(irb, cond_br_inst->scope, cond_br_inst->source_node, &peer_parent->base);
|
|
irb->current_basic_block->instruction_list.append(popped_inst);
|
|
|
|
return peer_parent;
|
|
}
|
|
|
|
static ResultLocPeerParent *ir_build_binary_result_peers(IrBuilder *irb, IrInstruction *cond_br_inst,
|
|
IrBasicBlock *else_block, IrBasicBlock *end_block, ResultLoc *parent, IrInstruction *is_comptime)
|
|
{
|
|
ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, parent, is_comptime);
|
|
|
|
peer_parent->peers.append(create_peer_result(peer_parent));
|
|
peer_parent->peers.last()->next_bb = else_block;
|
|
|
|
peer_parent->peers.append(create_peer_result(peer_parent));
|
|
peer_parent->peers.last()->next_bb = end_block;
|
|
|
|
return peer_parent;
|
|
}
|
|
|
|
static IrInstruction *ir_gen_orelse(IrBuilder *irb, Scope *parent_scope, AstNode *node, LVal lval,
|
|
ResultLoc *result_loc)
|
|
{
|
|
assert(node->type == NodeTypeBinOpExpr);
|
|
|
|
AstNode *op1_node = node->data.bin_op_expr.op1;
|
|
AstNode *op2_node = node->data.bin_op_expr.op2;
|
|
|
|
IrInstruction *maybe_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LValPtr, nullptr);
|
|
if (maybe_ptr == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
IrInstruction *maybe_val = ir_build_load_ptr(irb, parent_scope, node, maybe_ptr);
|
|
IrInstruction *is_non_null = ir_build_test_nonnull(irb, parent_scope, node, maybe_val);
|
|
|
|
IrInstruction *is_comptime;
|
|
if (ir_should_inline(irb->exec, parent_scope)) {
|
|
is_comptime = ir_build_const_bool(irb, parent_scope, node, true);
|
|
} else {
|
|
is_comptime = ir_build_test_comptime(irb, parent_scope, node, is_non_null);
|
|
}
|
|
|
|
IrBasicBlock *ok_block = ir_create_basic_block(irb, parent_scope, "OptionalNonNull");
|
|
IrBasicBlock *null_block = ir_create_basic_block(irb, parent_scope, "OptionalNull");
|
|
IrBasicBlock *end_block = ir_create_basic_block(irb, parent_scope, "OptionalEnd");
|
|
IrInstruction *cond_br_inst = ir_build_cond_br(irb, parent_scope, node, is_non_null, ok_block, null_block, is_comptime);
|
|
|
|
ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, ok_block, end_block,
|
|
result_loc, is_comptime);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, null_block);
|
|
IrInstruction *null_result = ir_gen_node_extra(irb, op2_node, parent_scope, LValNone,
|
|
&peer_parent->peers.at(0)->base);
|
|
if (null_result == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
IrBasicBlock *after_null_block = irb->current_basic_block;
|
|
if (!instr_is_unreachable(null_result))
|
|
ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime));
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, ok_block);
|
|
IrInstruction *unwrapped_ptr = ir_build_optional_unwrap_ptr(irb, parent_scope, node, maybe_ptr, false, false);
|
|
IrInstruction *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr);
|
|
ir_build_end_expr(irb, parent_scope, node, unwrapped_payload, &peer_parent->peers.at(1)->base);
|
|
IrBasicBlock *after_ok_block = irb->current_basic_block;
|
|
ir_build_br(irb, parent_scope, node, end_block, is_comptime);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, end_block);
|
|
IrInstruction **incoming_values = allocate<IrInstruction *>(2);
|
|
incoming_values[0] = null_result;
|
|
incoming_values[1] = unwrapped_payload;
|
|
IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
|
|
incoming_blocks[0] = after_null_block;
|
|
incoming_blocks[1] = after_ok_block;
|
|
IrInstruction *phi = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values, peer_parent);
|
|
return ir_lval_wrap(irb, parent_scope, phi, lval, result_loc);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_error_union(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
|
|
assert(node->type == NodeTypeBinOpExpr);
|
|
|
|
AstNode *op1_node = node->data.bin_op_expr.op1;
|
|
AstNode *op2_node = node->data.bin_op_expr.op2;
|
|
|
|
IrInstruction *err_set = ir_gen_node(irb, op1_node, parent_scope);
|
|
if (err_set == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
IrInstruction *payload = ir_gen_node(irb, op2_node, parent_scope);
|
|
if (payload == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
return ir_build_error_union(irb, parent_scope, node, err_set, payload);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) {
|
|
assert(node->type == NodeTypeBinOpExpr);
|
|
|
|
BinOpType bin_op_type = node->data.bin_op_expr.bin_op;
|
|
switch (bin_op_type) {
|
|
case BinOpTypeInvalid:
|
|
zig_unreachable();
|
|
case BinOpTypeAssign:
|
|
return ir_lval_wrap(irb, scope, ir_gen_assign(irb, scope, node), lval, result_loc);
|
|
case BinOpTypeAssignTimes:
|
|
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpMult), lval, result_loc);
|
|
case BinOpTypeAssignTimesWrap:
|
|
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpMultWrap), lval, result_loc);
|
|
case BinOpTypeAssignDiv:
|
|
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpDivUnspecified), lval, result_loc);
|
|
case BinOpTypeAssignMod:
|
|
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpRemUnspecified), lval, result_loc);
|
|
case BinOpTypeAssignPlus:
|
|
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpAdd), lval, result_loc);
|
|
case BinOpTypeAssignPlusWrap:
|
|
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpAddWrap), lval, result_loc);
|
|
case BinOpTypeAssignMinus:
|
|
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpSub), lval, result_loc);
|
|
case BinOpTypeAssignMinusWrap:
|
|
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpSubWrap), lval, result_loc);
|
|
case BinOpTypeAssignBitShiftLeft:
|
|
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftLeftLossy), lval, result_loc);
|
|
case BinOpTypeAssignBitShiftRight:
|
|
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc);
|
|
case BinOpTypeAssignBitAnd:
|
|
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBinAnd), lval, result_loc);
|
|
case BinOpTypeAssignBitXor:
|
|
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBinXor), lval, result_loc);
|
|
case BinOpTypeAssignBitOr:
|
|
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBinOr), lval, result_loc);
|
|
case BinOpTypeAssignMergeErrorSets:
|
|
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpMergeErrorSets), lval, result_loc);
|
|
case BinOpTypeBoolOr:
|
|
return ir_lval_wrap(irb, scope, ir_gen_bool_or(irb, scope, node), lval, result_loc);
|
|
case BinOpTypeBoolAnd:
|
|
return ir_lval_wrap(irb, scope, ir_gen_bool_and(irb, scope, node), lval, result_loc);
|
|
case BinOpTypeCmpEq:
|
|
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpEq), lval, result_loc);
|
|
case BinOpTypeCmpNotEq:
|
|
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpNotEq), lval, result_loc);
|
|
case BinOpTypeCmpLessThan:
|
|
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessThan), lval, result_loc);
|
|
case BinOpTypeCmpGreaterThan:
|
|
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterThan), lval, result_loc);
|
|
case BinOpTypeCmpLessOrEq:
|
|
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessOrEq), lval, result_loc);
|
|
case BinOpTypeCmpGreaterOrEq:
|
|
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterOrEq), lval, result_loc);
|
|
case BinOpTypeBinOr:
|
|
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBinOr), lval, result_loc);
|
|
case BinOpTypeBinXor:
|
|
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBinXor), lval, result_loc);
|
|
case BinOpTypeBinAnd:
|
|
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBinAnd), lval, result_loc);
|
|
case BinOpTypeBitShiftLeft:
|
|
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftLeftLossy), lval, result_loc);
|
|
case BinOpTypeBitShiftRight:
|
|
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc);
|
|
case BinOpTypeAdd:
|
|
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpAdd), lval, result_loc);
|
|
case BinOpTypeAddWrap:
|
|
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpAddWrap), lval, result_loc);
|
|
case BinOpTypeSub:
|
|
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpSub), lval, result_loc);
|
|
case BinOpTypeSubWrap:
|
|
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpSubWrap), lval, result_loc);
|
|
case BinOpTypeMult:
|
|
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpMult), lval, result_loc);
|
|
case BinOpTypeMultWrap:
|
|
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpMultWrap), lval, result_loc);
|
|
case BinOpTypeDiv:
|
|
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpDivUnspecified), lval, result_loc);
|
|
case BinOpTypeMod:
|
|
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpRemUnspecified), lval, result_loc);
|
|
case BinOpTypeArrayCat:
|
|
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayCat), lval, result_loc);
|
|
case BinOpTypeArrayMult:
|
|
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayMult), lval, result_loc);
|
|
case BinOpTypeMergeErrorSets:
|
|
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpMergeErrorSets), lval, result_loc);
|
|
case BinOpTypeUnwrapOptional:
|
|
return ir_gen_orelse(irb, scope, node, lval, result_loc);
|
|
case BinOpTypeErrorUnion:
|
|
return ir_lval_wrap(irb, scope, ir_gen_error_union(irb, scope, node), lval, result_loc);
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
static IrInstruction *ir_gen_int_lit(IrBuilder *irb, Scope *scope, AstNode *node) {
|
|
assert(node->type == NodeTypeIntLiteral);
|
|
|
|
return ir_build_const_bigint(irb, scope, node, node->data.int_literal.bigint);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_float_lit(IrBuilder *irb, Scope *scope, AstNode *node) {
|
|
assert(node->type == NodeTypeFloatLiteral);
|
|
|
|
if (node->data.float_literal.overflow) {
|
|
add_node_error(irb->codegen, node, buf_sprintf("float literal out of range of any type"));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
|
|
return ir_build_const_bigfloat(irb, scope, node, node->data.float_literal.bigfloat);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_char_lit(IrBuilder *irb, Scope *scope, AstNode *node) {
|
|
assert(node->type == NodeTypeCharLiteral);
|
|
|
|
return ir_build_const_uint(irb, scope, node, node->data.char_literal.value);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_null_literal(IrBuilder *irb, Scope *scope, AstNode *node) {
|
|
assert(node->type == NodeTypeNullLiteral);
|
|
|
|
return ir_build_const_null(irb, scope, node);
|
|
}
|
|
|
|
static void populate_invalid_variable_in_scope(CodeGen *g, Scope *scope, AstNode *node, Buf *var_name) {
|
|
ScopeDecls *scope_decls = nullptr;
|
|
while (scope != nullptr) {
|
|
if (scope->id == ScopeIdDecls) {
|
|
scope_decls = reinterpret_cast<ScopeDecls *>(scope);
|
|
}
|
|
scope = scope->parent;
|
|
}
|
|
TldVar *tld_var = allocate<TldVar>(1);
|
|
init_tld(&tld_var->base, TldIdVar, var_name, VisibModPub, node, &scope_decls->base);
|
|
tld_var->base.resolution = TldResolutionInvalid;
|
|
tld_var->var = add_variable(g, node, &scope_decls->base, var_name, false,
|
|
&g->invalid_instruction->value, &tld_var->base, g->builtin_types.entry_invalid);
|
|
scope_decls->decl_table.put(var_name, &tld_var->base);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) {
|
|
Error err;
|
|
assert(node->type == NodeTypeSymbol);
|
|
|
|
Buf *variable_name = node->data.symbol_expr.symbol;
|
|
|
|
if (buf_eql_str(variable_name, "_")) {
|
|
if (lval == LValPtr) {
|
|
IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, node);
|
|
const_instruction->base.value.type = get_pointer_to_type(irb->codegen,
|
|
irb->codegen->builtin_types.entry_void, false);
|
|
const_instruction->base.value.special = ConstValSpecialStatic;
|
|
const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialDiscard;
|
|
return &const_instruction->base;
|
|
} else {
|
|
add_node_error(irb->codegen, node, buf_sprintf("`_` may only be used to assign things to"));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
|
|
ZigType *primitive_type;
|
|
if ((err = get_primitive_type(irb->codegen, variable_name, &primitive_type))) {
|
|
if (err == ErrorOverflow) {
|
|
add_node_error(irb->codegen, node,
|
|
buf_sprintf("primitive integer type '%s' exceeds maximum bit width of 65535",
|
|
buf_ptr(variable_name)));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
assert(err == ErrorPrimitiveTypeNotFound);
|
|
} else {
|
|
IrInstruction *value = ir_build_const_type(irb, scope, node, primitive_type);
|
|
if (lval == LValPtr) {
|
|
return ir_build_ref(irb, scope, node, value, false, false);
|
|
} else {
|
|
return ir_expr_wrap(irb, scope, value, result_loc);
|
|
}
|
|
}
|
|
|
|
ScopeFnDef *crossed_fndef_scope;
|
|
ZigVar *var = find_variable(irb->codegen, scope, variable_name, &crossed_fndef_scope);
|
|
if (var) {
|
|
IrInstruction *var_ptr = ir_build_var_ptr_x(irb, scope, node, var, crossed_fndef_scope);
|
|
if (lval == LValPtr) {
|
|
return var_ptr;
|
|
} else {
|
|
return ir_expr_wrap(irb, scope, ir_build_load_ptr(irb, scope, node, var_ptr), result_loc);
|
|
}
|
|
}
|
|
|
|
Tld *tld = find_decl(irb->codegen, scope, variable_name);
|
|
if (tld) {
|
|
IrInstruction *decl_ref = ir_build_decl_ref(irb, scope, node, tld, lval);
|
|
if (lval == LValPtr) {
|
|
return decl_ref;
|
|
} else {
|
|
return ir_expr_wrap(irb, scope, decl_ref, result_loc);
|
|
}
|
|
}
|
|
|
|
if (get_container_scope(node->owner)->any_imports_failed) {
|
|
// skip the error message since we had a failing import in this file
|
|
// if an import breaks we don't need redundant undeclared identifier errors
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
|
|
return ir_build_undeclared_identifier(irb, scope, node, variable_name);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_array_access(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval,
|
|
ResultLoc *result_loc)
|
|
{
|
|
assert(node->type == NodeTypeArrayAccessExpr);
|
|
|
|
AstNode *array_ref_node = node->data.array_access_expr.array_ref_expr;
|
|
IrInstruction *array_ref_instruction = ir_gen_node_extra(irb, array_ref_node, scope, LValPtr, nullptr);
|
|
if (array_ref_instruction == irb->codegen->invalid_instruction)
|
|
return array_ref_instruction;
|
|
|
|
AstNode *subscript_node = node->data.array_access_expr.subscript;
|
|
IrInstruction *subscript_instruction = ir_gen_node(irb, subscript_node, scope);
|
|
if (subscript_instruction == irb->codegen->invalid_instruction)
|
|
return subscript_instruction;
|
|
|
|
IrInstruction *ptr_instruction = ir_build_elem_ptr(irb, scope, node, array_ref_instruction,
|
|
subscript_instruction, true, PtrLenSingle, nullptr);
|
|
if (lval == LValPtr)
|
|
return ptr_instruction;
|
|
|
|
IrInstruction *load_ptr = ir_build_load_ptr(irb, scope, node, ptr_instruction);
|
|
return ir_expr_wrap(irb, scope, load_ptr, result_loc);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode *node) {
|
|
assert(node->type == NodeTypeFieldAccessExpr);
|
|
|
|
AstNode *container_ref_node = node->data.field_access_expr.struct_expr;
|
|
Buf *field_name = node->data.field_access_expr.field_name;
|
|
|
|
IrInstruction *container_ref_instruction = ir_gen_node_extra(irb, container_ref_node, scope, LValPtr, nullptr);
|
|
if (container_ref_instruction == irb->codegen->invalid_instruction)
|
|
return container_ref_instruction;
|
|
|
|
return ir_build_field_ptr(irb, scope, node, container_ref_instruction, field_name, false);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_overflow_op(IrBuilder *irb, Scope *scope, AstNode *node, IrOverflowOp op) {
|
|
assert(node->type == NodeTypeFnCallExpr);
|
|
|
|
AstNode *type_node = node->data.fn_call_expr.params.at(0);
|
|
AstNode *op1_node = node->data.fn_call_expr.params.at(1);
|
|
AstNode *op2_node = node->data.fn_call_expr.params.at(2);
|
|
AstNode *result_ptr_node = node->data.fn_call_expr.params.at(3);
|
|
|
|
|
|
IrInstruction *type_value = ir_gen_node(irb, type_node, scope);
|
|
if (type_value == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
IrInstruction *op1 = ir_gen_node(irb, op1_node, scope);
|
|
if (op1 == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
IrInstruction *op2 = ir_gen_node(irb, op2_node, scope);
|
|
if (op2 == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
IrInstruction *result_ptr = ir_gen_node(irb, result_ptr_node, scope);
|
|
if (result_ptr == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
return ir_build_overflow_op(irb, scope, node, op, type_value, op1, op2, result_ptr, nullptr);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_mul_add(IrBuilder *irb, Scope *scope, AstNode *node) {
|
|
assert(node->type == NodeTypeFnCallExpr);
|
|
|
|
AstNode *type_node = node->data.fn_call_expr.params.at(0);
|
|
AstNode *op1_node = node->data.fn_call_expr.params.at(1);
|
|
AstNode *op2_node = node->data.fn_call_expr.params.at(2);
|
|
AstNode *op3_node = node->data.fn_call_expr.params.at(3);
|
|
|
|
IrInstruction *type_value = ir_gen_node(irb, type_node, scope);
|
|
if (type_value == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
IrInstruction *op1 = ir_gen_node(irb, op1_node, scope);
|
|
if (op1 == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
IrInstruction *op2 = ir_gen_node(irb, op2_node, scope);
|
|
if (op2 == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
IrInstruction *op3 = ir_gen_node(irb, op3_node, scope);
|
|
if (op3 == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
return ir_build_mul_add(irb, scope, node, type_value, op1, op2, op3);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_this(IrBuilder *irb, Scope *orig_scope, AstNode *node) {
|
|
for (Scope *it_scope = orig_scope; it_scope != nullptr; it_scope = it_scope->parent) {
|
|
if (it_scope->id == ScopeIdDecls) {
|
|
ScopeDecls *decls_scope = (ScopeDecls *)it_scope;
|
|
ZigType *container_type = decls_scope->container_type;
|
|
if (container_type != nullptr) {
|
|
return ir_build_const_type(irb, orig_scope, node, container_type);
|
|
} else {
|
|
return ir_build_const_import(irb, orig_scope, node, decls_scope->import);
|
|
}
|
|
}
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval,
|
|
ResultLoc *result_loc)
|
|
{
|
|
assert(node->type == NodeTypeFnCallExpr);
|
|
|
|
AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
|
|
Buf *name = fn_ref_expr->data.symbol_expr.symbol;
|
|
auto entry = irb->codegen->builtin_fn_table.maybe_get(name);
|
|
|
|
if (!entry) { // new built in not found
|
|
add_node_error(irb->codegen, node,
|
|
buf_sprintf("invalid builtin function: '%s'", buf_ptr(name)));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
|
|
BuiltinFnEntry *builtin_fn = entry->value;
|
|
size_t actual_param_count = node->data.fn_call_expr.params.length;
|
|
|
|
if (builtin_fn->param_count != SIZE_MAX && builtin_fn->param_count != actual_param_count) {
|
|
add_node_error(irb->codegen, node,
|
|
buf_sprintf("expected %" ZIG_PRI_usize " arguments, found %" ZIG_PRI_usize,
|
|
builtin_fn->param_count, actual_param_count));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
|
|
switch (builtin_fn->id) {
|
|
case BuiltinFnIdInvalid:
|
|
zig_unreachable();
|
|
case BuiltinFnIdTypeof:
|
|
{
|
|
AstNode *arg_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg = ir_gen_node(irb, arg_node, scope);
|
|
if (arg == irb->codegen->invalid_instruction)
|
|
return arg;
|
|
|
|
IrInstruction *type_of = ir_build_typeof(irb, scope, node, arg);
|
|
return ir_lval_wrap(irb, scope, type_of, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdSetCold:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
IrInstruction *set_cold = ir_build_set_cold(irb, scope, node, arg0_value);
|
|
return ir_lval_wrap(irb, scope, set_cold, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdSetRuntimeSafety:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
IrInstruction *set_safety = ir_build_set_runtime_safety(irb, scope, node, arg0_value);
|
|
return ir_lval_wrap(irb, scope, set_safety, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdSetFloatMode:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
IrInstruction *set_float_mode = ir_build_set_float_mode(irb, scope, node, arg0_value);
|
|
return ir_lval_wrap(irb, scope, set_float_mode, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdSizeof:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
IrInstruction *size_of = ir_build_size_of(irb, scope, node, arg0_value);
|
|
return ir_lval_wrap(irb, scope, size_of, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdImport:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
IrInstruction *import = ir_build_import(irb, scope, node, arg0_value);
|
|
return ir_lval_wrap(irb, scope, import, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdCImport:
|
|
{
|
|
IrInstruction *c_import = ir_build_c_import(irb, scope, node);
|
|
return ir_lval_wrap(irb, scope, c_import, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdCInclude:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
if (!exec_c_import_buf(irb->exec)) {
|
|
add_node_error(irb->codegen, node, buf_sprintf("C include valid only inside C import block"));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *c_include = ir_build_c_include(irb, scope, node, arg0_value);
|
|
return ir_lval_wrap(irb, scope, c_include, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdCDefine:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
if (!exec_c_import_buf(irb->exec)) {
|
|
add_node_error(irb->codegen, node, buf_sprintf("C define valid only inside C import block"));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *c_define = ir_build_c_define(irb, scope, node, arg0_value, arg1_value);
|
|
return ir_lval_wrap(irb, scope, c_define, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdCUndef:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
if (!exec_c_import_buf(irb->exec)) {
|
|
add_node_error(irb->codegen, node, buf_sprintf("C undef valid only inside C import block"));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *c_undef = ir_build_c_undef(irb, scope, node, arg0_value);
|
|
return ir_lval_wrap(irb, scope, c_undef, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdCompileErr:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
IrInstruction *compile_err = ir_build_compile_err(irb, scope, node, arg0_value);
|
|
return ir_lval_wrap(irb, scope, compile_err, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdCompileLog:
|
|
{
|
|
IrInstruction **args = allocate<IrInstruction*>(actual_param_count);
|
|
|
|
for (size_t i = 0; i < actual_param_count; i += 1) {
|
|
AstNode *arg_node = node->data.fn_call_expr.params.at(i);
|
|
args[i] = ir_gen_node(irb, arg_node, scope);
|
|
if (args[i] == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *compile_log = ir_build_compile_log(irb, scope, node, actual_param_count, args);
|
|
return ir_lval_wrap(irb, scope, compile_log, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdErrName:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
IrInstruction *err_name = ir_build_err_name(irb, scope, node, arg0_value);
|
|
return ir_lval_wrap(irb, scope, err_name, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdEmbedFile:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
IrInstruction *embed_file = ir_build_embed_file(irb, scope, node, arg0_value);
|
|
return ir_lval_wrap(irb, scope, embed_file, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdCmpxchgWeak:
|
|
case BuiltinFnIdCmpxchgStrong:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
|
|
IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope);
|
|
if (arg2_value == irb->codegen->invalid_instruction)
|
|
return arg2_value;
|
|
|
|
AstNode *arg3_node = node->data.fn_call_expr.params.at(3);
|
|
IrInstruction *arg3_value = ir_gen_node(irb, arg3_node, scope);
|
|
if (arg3_value == irb->codegen->invalid_instruction)
|
|
return arg3_value;
|
|
|
|
AstNode *arg4_node = node->data.fn_call_expr.params.at(4);
|
|
IrInstruction *arg4_value = ir_gen_node(irb, arg4_node, scope);
|
|
if (arg4_value == irb->codegen->invalid_instruction)
|
|
return arg4_value;
|
|
|
|
AstNode *arg5_node = node->data.fn_call_expr.params.at(5);
|
|
IrInstruction *arg5_value = ir_gen_node(irb, arg5_node, scope);
|
|
if (arg5_value == irb->codegen->invalid_instruction)
|
|
return arg5_value;
|
|
|
|
IrInstruction *cmpxchg = ir_build_cmpxchg_src(irb, scope, node, arg0_value, arg1_value,
|
|
arg2_value, arg3_value, arg4_value, arg5_value, (builtin_fn->id == BuiltinFnIdCmpxchgWeak),
|
|
result_loc);
|
|
return ir_lval_wrap(irb, scope, cmpxchg, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdFence:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
IrInstruction *fence = ir_build_fence(irb, scope, node, arg0_value, AtomicOrderUnordered);
|
|
return ir_lval_wrap(irb, scope, fence, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdDivExact:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivExact, arg0_value, arg1_value, true);
|
|
return ir_lval_wrap(irb, scope, bin_op, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdDivTrunc:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivTrunc, arg0_value, arg1_value, true);
|
|
return ir_lval_wrap(irb, scope, bin_op, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdDivFloor:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivFloor, arg0_value, arg1_value, true);
|
|
return ir_lval_wrap(irb, scope, bin_op, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdRem:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpRemRem, arg0_value, arg1_value, true);
|
|
return ir_lval_wrap(irb, scope, bin_op, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdMod:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpRemMod, arg0_value, arg1_value, true);
|
|
return ir_lval_wrap(irb, scope, bin_op, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdSqrt:
|
|
case BuiltinFnIdSin:
|
|
case BuiltinFnIdCos:
|
|
case BuiltinFnIdExp:
|
|
case BuiltinFnIdExp2:
|
|
case BuiltinFnIdLn:
|
|
case BuiltinFnIdLog2:
|
|
case BuiltinFnIdLog10:
|
|
case BuiltinFnIdFabs:
|
|
case BuiltinFnIdFloor:
|
|
case BuiltinFnIdCeil:
|
|
case BuiltinFnIdTrunc:
|
|
case BuiltinFnIdNearbyInt:
|
|
case BuiltinFnIdRound:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
IrInstruction *ir_sqrt = ir_build_float_op(irb, scope, node, arg0_value, arg1_value, builtin_fn->id);
|
|
return ir_lval_wrap(irb, scope, ir_sqrt, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdTruncate:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
IrInstruction *truncate = ir_build_truncate(irb, scope, node, arg0_value, arg1_value);
|
|
return ir_lval_wrap(irb, scope, truncate, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdIntCast:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
IrInstruction *result = ir_build_int_cast(irb, scope, node, arg0_value, arg1_value);
|
|
return ir_lval_wrap(irb, scope, result, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdFloatCast:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
IrInstruction *result = ir_build_float_cast(irb, scope, node, arg0_value, arg1_value);
|
|
return ir_lval_wrap(irb, scope, result, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdErrSetCast:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
IrInstruction *result = ir_build_err_set_cast(irb, scope, node, arg0_value, arg1_value);
|
|
return ir_lval_wrap(irb, scope, result, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdFromBytes:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
IrInstruction *result = ir_build_from_bytes(irb, scope, node, arg0_value, arg1_value, result_loc);
|
|
return ir_lval_wrap(irb, scope, result, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdToBytes:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
IrInstruction *result = ir_build_to_bytes(irb, scope, node, arg0_value, result_loc);
|
|
return ir_lval_wrap(irb, scope, result, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdIntToFloat:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
IrInstruction *result = ir_build_int_to_float(irb, scope, node, arg0_value, arg1_value);
|
|
return ir_lval_wrap(irb, scope, result, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdFloatToInt:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
IrInstruction *result = ir_build_float_to_int(irb, scope, node, arg0_value, arg1_value);
|
|
return ir_lval_wrap(irb, scope, result, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdErrToInt:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
IrInstruction *result = ir_build_err_to_int(irb, scope, node, arg0_value);
|
|
return ir_lval_wrap(irb, scope, result, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdIntToErr:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
IrInstruction *result = ir_build_int_to_err(irb, scope, node, arg0_value);
|
|
return ir_lval_wrap(irb, scope, result, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdBoolToInt:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
IrInstruction *result = ir_build_bool_to_int(irb, scope, node, arg0_value);
|
|
return ir_lval_wrap(irb, scope, result, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdIntType:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
IrInstruction *int_type = ir_build_int_type(irb, scope, node, arg0_value, arg1_value);
|
|
return ir_lval_wrap(irb, scope, int_type, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdVectorType:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
IrInstruction *vector_type = ir_build_vector_type(irb, scope, node, arg0_value, arg1_value);
|
|
return ir_lval_wrap(irb, scope, vector_type, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdMemcpy:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
|
|
IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope);
|
|
if (arg2_value == irb->codegen->invalid_instruction)
|
|
return arg2_value;
|
|
|
|
IrInstruction *ir_memcpy = ir_build_memcpy(irb, scope, node, arg0_value, arg1_value, arg2_value);
|
|
return ir_lval_wrap(irb, scope, ir_memcpy, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdMemset:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
|
|
IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope);
|
|
if (arg2_value == irb->codegen->invalid_instruction)
|
|
return arg2_value;
|
|
|
|
IrInstruction *ir_memset = ir_build_memset(irb, scope, node, arg0_value, arg1_value, arg2_value);
|
|
return ir_lval_wrap(irb, scope, ir_memset, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdMemberCount:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
IrInstruction *member_count = ir_build_member_count(irb, scope, node, arg0_value);
|
|
return ir_lval_wrap(irb, scope, member_count, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdMemberType:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
|
|
IrInstruction *member_type = ir_build_member_type(irb, scope, node, arg0_value, arg1_value);
|
|
return ir_lval_wrap(irb, scope, member_type, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdMemberName:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
|
|
IrInstruction *member_name = ir_build_member_name(irb, scope, node, arg0_value, arg1_value);
|
|
return ir_lval_wrap(irb, scope, member_name, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdField:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node_extra(irb, arg0_node, scope, LValPtr, nullptr);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
IrInstruction *ptr_instruction = ir_build_field_ptr_instruction(irb, scope, node,
|
|
arg0_value, arg1_value, false);
|
|
|
|
if (lval == LValPtr)
|
|
return ptr_instruction;
|
|
|
|
IrInstruction *load_ptr = ir_build_load_ptr(irb, scope, node, ptr_instruction);
|
|
return ir_expr_wrap(irb, scope, load_ptr, result_loc);
|
|
}
|
|
case BuiltinFnIdHasField:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
IrInstruction *type_info = ir_build_has_field(irb, scope, node, arg0_value, arg1_value);
|
|
return ir_lval_wrap(irb, scope, type_info, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdTypeInfo:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
IrInstruction *type_info = ir_build_type_info(irb, scope, node, arg0_value);
|
|
return ir_lval_wrap(irb, scope, type_info, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdBreakpoint:
|
|
return ir_lval_wrap(irb, scope, ir_build_breakpoint(irb, scope, node), lval, result_loc);
|
|
case BuiltinFnIdReturnAddress:
|
|
return ir_lval_wrap(irb, scope, ir_build_return_address(irb, scope, node), lval, result_loc);
|
|
case BuiltinFnIdFrameAddress:
|
|
return ir_lval_wrap(irb, scope, ir_build_frame_address(irb, scope, node), lval, result_loc);
|
|
case BuiltinFnIdFrameHandle:
|
|
if (!irb->exec->fn_entry) {
|
|
add_node_error(irb->codegen, node, buf_sprintf("@handle() called outside of function definition"));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
return ir_lval_wrap(irb, scope, ir_build_handle(irb, scope, node), lval, result_loc);
|
|
case BuiltinFnIdFrameType: {
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
IrInstruction *frame_type = ir_build_frame_type(irb, scope, node, arg0_value);
|
|
return ir_lval_wrap(irb, scope, frame_type, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdFrameSize: {
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
IrInstruction *frame_size = ir_build_frame_size_src(irb, scope, node, arg0_value);
|
|
return ir_lval_wrap(irb, scope, frame_size, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdAlignOf:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
IrInstruction *align_of = ir_build_align_of(irb, scope, node, arg0_value);
|
|
return ir_lval_wrap(irb, scope, align_of, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdAddWithOverflow:
|
|
return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpAdd), lval, result_loc);
|
|
case BuiltinFnIdSubWithOverflow:
|
|
return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpSub), lval, result_loc);
|
|
case BuiltinFnIdMulWithOverflow:
|
|
return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul), lval, result_loc);
|
|
case BuiltinFnIdShlWithOverflow:
|
|
return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl), lval, result_loc);
|
|
case BuiltinFnIdMulAdd:
|
|
return ir_lval_wrap(irb, scope, ir_gen_mul_add(irb, scope, node), lval, result_loc);
|
|
case BuiltinFnIdTypeName:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
IrInstruction *type_name = ir_build_type_name(irb, scope, node, arg0_value);
|
|
return ir_lval_wrap(irb, scope, type_name, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdPanic:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
IrInstruction *panic = ir_build_panic(irb, scope, node, arg0_value);
|
|
return ir_lval_wrap(irb, scope, panic, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdPtrCast:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
IrInstruction *ptr_cast = ir_build_ptr_cast_src(irb, scope, node, arg0_value, arg1_value, true);
|
|
return ir_lval_wrap(irb, scope, ptr_cast, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdBitCast:
|
|
{
|
|
AstNode *dest_type_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *dest_type = ir_gen_node(irb, dest_type_node, scope);
|
|
if (dest_type == irb->codegen->invalid_instruction)
|
|
return dest_type;
|
|
|
|
ResultLocBitCast *result_loc_bit_cast = allocate<ResultLocBitCast>(1);
|
|
result_loc_bit_cast->base.id = ResultLocIdBitCast;
|
|
result_loc_bit_cast->base.source_instruction = dest_type;
|
|
ir_ref_instruction(dest_type, irb->current_basic_block);
|
|
result_loc_bit_cast->parent = result_loc;
|
|
|
|
ir_build_reset_result(irb, scope, node, &result_loc_bit_cast->base);
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node_extra(irb, arg1_node, scope, LValNone,
|
|
&result_loc_bit_cast->base);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
IrInstruction *bitcast = ir_build_bit_cast_src(irb, scope, arg1_node, arg1_value, result_loc_bit_cast);
|
|
return ir_lval_wrap(irb, scope, bitcast, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdIntToPtr:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
IrInstruction *int_to_ptr = ir_build_int_to_ptr(irb, scope, node, arg0_value, arg1_value);
|
|
return ir_lval_wrap(irb, scope, int_to_ptr, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdPtrToInt:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
IrInstruction *ptr_to_int = ir_build_ptr_to_int(irb, scope, node, arg0_value);
|
|
return ir_lval_wrap(irb, scope, ptr_to_int, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdTagName:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
IrInstruction *actual_tag = ir_build_union_tag(irb, scope, node, arg0_value);
|
|
IrInstruction *tag_name = ir_build_tag_name(irb, scope, node, actual_tag);
|
|
return ir_lval_wrap(irb, scope, tag_name, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdTagType:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
IrInstruction *tag_type = ir_build_tag_type(irb, scope, node, arg0_value);
|
|
return ir_lval_wrap(irb, scope, tag_type, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdFieldParentPtr:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
|
|
IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope);
|
|
if (arg2_value == irb->codegen->invalid_instruction)
|
|
return arg2_value;
|
|
|
|
IrInstruction *field_parent_ptr = ir_build_field_parent_ptr(irb, scope, node, arg0_value, arg1_value, arg2_value, nullptr);
|
|
return ir_lval_wrap(irb, scope, field_parent_ptr, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdByteOffsetOf:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
IrInstruction *offset_of = ir_build_byte_offset_of(irb, scope, node, arg0_value, arg1_value);
|
|
return ir_lval_wrap(irb, scope, offset_of, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdBitOffsetOf:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
IrInstruction *offset_of = ir_build_bit_offset_of(irb, scope, node, arg0_value, arg1_value);
|
|
return ir_lval_wrap(irb, scope, offset_of, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdInlineCall:
|
|
case BuiltinFnIdNoInlineCall:
|
|
{
|
|
if (node->data.fn_call_expr.params.length == 0) {
|
|
add_node_error(irb->codegen, node, buf_sprintf("expected at least 1 argument, found 0"));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
|
|
AstNode *fn_ref_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope);
|
|
if (fn_ref == irb->codegen->invalid_instruction)
|
|
return fn_ref;
|
|
|
|
size_t arg_count = node->data.fn_call_expr.params.length - 1;
|
|
|
|
IrInstruction **args = allocate<IrInstruction*>(arg_count);
|
|
for (size_t i = 0; i < arg_count; i += 1) {
|
|
AstNode *arg_node = node->data.fn_call_expr.params.at(i + 1);
|
|
args[i] = ir_gen_node(irb, arg_node, scope);
|
|
if (args[i] == irb->codegen->invalid_instruction)
|
|
return args[i];
|
|
}
|
|
FnInline fn_inline = (builtin_fn->id == BuiltinFnIdInlineCall) ? FnInlineAlways : FnInlineNever;
|
|
|
|
IrInstruction *call = ir_build_call_src(irb, scope, node, nullptr, fn_ref, arg_count, args, false,
|
|
fn_inline, false, nullptr, result_loc);
|
|
return ir_lval_wrap(irb, scope, call, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdNewStackCall:
|
|
{
|
|
if (node->data.fn_call_expr.params.length < 2) {
|
|
add_node_error(irb->codegen, node,
|
|
buf_sprintf("expected at least 2 arguments, found %" ZIG_PRI_usize,
|
|
node->data.fn_call_expr.params.length));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
|
|
AstNode *new_stack_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *new_stack = ir_gen_node(irb, new_stack_node, scope);
|
|
if (new_stack == irb->codegen->invalid_instruction)
|
|
return new_stack;
|
|
|
|
AstNode *fn_ref_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope);
|
|
if (fn_ref == irb->codegen->invalid_instruction)
|
|
return fn_ref;
|
|
|
|
size_t arg_count = node->data.fn_call_expr.params.length - 2;
|
|
|
|
IrInstruction **args = allocate<IrInstruction*>(arg_count);
|
|
for (size_t i = 0; i < arg_count; i += 1) {
|
|
AstNode *arg_node = node->data.fn_call_expr.params.at(i + 2);
|
|
args[i] = ir_gen_node(irb, arg_node, scope);
|
|
if (args[i] == irb->codegen->invalid_instruction)
|
|
return args[i];
|
|
}
|
|
|
|
IrInstruction *call = ir_build_call_src(irb, scope, node, nullptr, fn_ref, arg_count, args, false,
|
|
FnInlineAuto, false, new_stack, result_loc);
|
|
return ir_lval_wrap(irb, scope, call, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdAsyncCall:
|
|
{
|
|
size_t arg_offset = 3;
|
|
if (node->data.fn_call_expr.params.length < arg_offset) {
|
|
add_node_error(irb->codegen, node,
|
|
buf_sprintf("expected at least %" ZIG_PRI_usize " arguments, found %" ZIG_PRI_usize,
|
|
arg_offset, node->data.fn_call_expr.params.length));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
|
|
AstNode *bytes_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *bytes = ir_gen_node(irb, bytes_node, scope);
|
|
if (bytes == irb->codegen->invalid_instruction)
|
|
return bytes;
|
|
|
|
AstNode *ret_ptr_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *ret_ptr = ir_gen_node(irb, ret_ptr_node, scope);
|
|
if (ret_ptr == irb->codegen->invalid_instruction)
|
|
return ret_ptr;
|
|
|
|
AstNode *fn_ref_node = node->data.fn_call_expr.params.at(2);
|
|
IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope);
|
|
if (fn_ref == irb->codegen->invalid_instruction)
|
|
return fn_ref;
|
|
|
|
size_t arg_count = node->data.fn_call_expr.params.length - arg_offset;
|
|
|
|
// last "arg" is return pointer
|
|
IrInstruction **args = allocate<IrInstruction*>(arg_count + 1);
|
|
|
|
for (size_t i = 0; i < arg_count; i += 1) {
|
|
AstNode *arg_node = node->data.fn_call_expr.params.at(i + arg_offset);
|
|
IrInstruction *arg = ir_gen_node(irb, arg_node, scope);
|
|
if (arg == irb->codegen->invalid_instruction)
|
|
return arg;
|
|
args[i] = arg;
|
|
}
|
|
|
|
args[arg_count] = ret_ptr;
|
|
|
|
IrInstruction *call = ir_build_call_src(irb, scope, node, nullptr, fn_ref, arg_count, args, false,
|
|
FnInlineAuto, true, bytes, result_loc);
|
|
return ir_lval_wrap(irb, scope, call, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdTypeId:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
IrInstruction *type_id = ir_build_type_id(irb, scope, node, arg0_value);
|
|
return ir_lval_wrap(irb, scope, type_id, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdShlExact:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftLeftExact, arg0_value, arg1_value, true);
|
|
return ir_lval_wrap(irb, scope, bin_op, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdShrExact:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftRightExact, arg0_value, arg1_value, true);
|
|
return ir_lval_wrap(irb, scope, bin_op, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdSetEvalBranchQuota:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
IrInstruction *set_eval_branch_quota = ir_build_set_eval_branch_quota(irb, scope, node, arg0_value);
|
|
return ir_lval_wrap(irb, scope, set_eval_branch_quota, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdAlignCast:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
IrInstruction *align_cast = ir_build_align_cast(irb, scope, node, arg0_value, arg1_value);
|
|
return ir_lval_wrap(irb, scope, align_cast, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdOpaqueType:
|
|
{
|
|
IrInstruction *opaque_type = ir_build_opaque_type(irb, scope, node);
|
|
return ir_lval_wrap(irb, scope, opaque_type, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdThis:
|
|
{
|
|
IrInstruction *this_inst = ir_gen_this(irb, scope, node);
|
|
return ir_lval_wrap(irb, scope, this_inst, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdSetAlignStack:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
IrInstruction *set_align_stack = ir_build_set_align_stack(irb, scope, node, arg0_value);
|
|
return ir_lval_wrap(irb, scope, set_align_stack, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdArgType:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
IrInstruction *arg_type = ir_build_arg_type(irb, scope, node, arg0_value, arg1_value);
|
|
return ir_lval_wrap(irb, scope, arg_type, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdExport:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
|
|
IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope);
|
|
if (arg2_value == irb->codegen->invalid_instruction)
|
|
return arg2_value;
|
|
|
|
IrInstruction *ir_export = ir_build_export(irb, scope, node, arg0_value, arg1_value, arg2_value);
|
|
return ir_lval_wrap(irb, scope, ir_export, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdErrorReturnTrace:
|
|
{
|
|
IrInstruction *error_return_trace = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::Null);
|
|
return ir_lval_wrap(irb, scope, error_return_trace, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdAtomicRmw:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
|
|
IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope);
|
|
if (arg2_value == irb->codegen->invalid_instruction)
|
|
return arg2_value;
|
|
|
|
AstNode *arg3_node = node->data.fn_call_expr.params.at(3);
|
|
IrInstruction *arg3_value = ir_gen_node(irb, arg3_node, scope);
|
|
if (arg3_value == irb->codegen->invalid_instruction)
|
|
return arg3_value;
|
|
|
|
AstNode *arg4_node = node->data.fn_call_expr.params.at(4);
|
|
IrInstruction *arg4_value = ir_gen_node(irb, arg4_node, scope);
|
|
if (arg4_value == irb->codegen->invalid_instruction)
|
|
return arg4_value;
|
|
|
|
IrInstruction *inst = ir_build_atomic_rmw(irb, scope, node, arg0_value, arg1_value, arg2_value, arg3_value,
|
|
arg4_value,
|
|
// these 2 values don't mean anything since we passed non-null values for other args
|
|
AtomicRmwOp_xchg, AtomicOrderMonotonic);
|
|
return ir_lval_wrap(irb, scope, inst, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdAtomicLoad:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
|
|
IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope);
|
|
if (arg2_value == irb->codegen->invalid_instruction)
|
|
return arg2_value;
|
|
|
|
IrInstruction *inst = ir_build_atomic_load(irb, scope, node, arg0_value, arg1_value, arg2_value,
|
|
// this value does not mean anything since we passed non-null values for other arg
|
|
AtomicOrderMonotonic);
|
|
return ir_lval_wrap(irb, scope, inst, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdIntToEnum:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
IrInstruction *result = ir_build_int_to_enum(irb, scope, node, arg0_value, arg1_value);
|
|
return ir_lval_wrap(irb, scope, result, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdEnumToInt:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
IrInstruction *result = ir_build_enum_to_int(irb, scope, node, arg0_value);
|
|
return ir_lval_wrap(irb, scope, result, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdCtz:
|
|
case BuiltinFnIdPopCount:
|
|
case BuiltinFnIdClz:
|
|
case BuiltinFnIdBswap:
|
|
case BuiltinFnIdBitReverse:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
IrInstruction *result;
|
|
switch (builtin_fn->id) {
|
|
case BuiltinFnIdCtz:
|
|
result = ir_build_ctz(irb, scope, node, arg0_value, arg1_value);
|
|
break;
|
|
case BuiltinFnIdPopCount:
|
|
result = ir_build_pop_count(irb, scope, node, arg0_value, arg1_value);
|
|
break;
|
|
case BuiltinFnIdClz:
|
|
result = ir_build_clz(irb, scope, node, arg0_value, arg1_value);
|
|
break;
|
|
case BuiltinFnIdBswap:
|
|
result = ir_build_bswap(irb, scope, node, arg0_value, arg1_value);
|
|
break;
|
|
case BuiltinFnIdBitReverse:
|
|
result = ir_build_bit_reverse(irb, scope, node, arg0_value, arg1_value);
|
|
break;
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
return ir_lval_wrap(irb, scope, result, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdHasDecl:
|
|
{
|
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
|
if (arg0_value == irb->codegen->invalid_instruction)
|
|
return arg0_value;
|
|
|
|
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
|
if (arg1_value == irb->codegen->invalid_instruction)
|
|
return arg1_value;
|
|
|
|
IrInstruction *has_decl = ir_build_has_decl(irb, scope, node, arg0_value, arg1_value);
|
|
return ir_lval_wrap(irb, scope, has_decl, lval, result_loc);
|
|
}
|
|
case BuiltinFnIdUnionInit:
|
|
{
|
|
AstNode *union_type_node = node->data.fn_call_expr.params.at(0);
|
|
IrInstruction *union_type_inst = ir_gen_node(irb, union_type_node, scope);
|
|
if (union_type_inst == irb->codegen->invalid_instruction)
|
|
return union_type_inst;
|
|
|
|
AstNode *name_node = node->data.fn_call_expr.params.at(1);
|
|
IrInstruction *name_inst = ir_gen_node(irb, name_node, scope);
|
|
if (name_inst == irb->codegen->invalid_instruction)
|
|
return name_inst;
|
|
|
|
AstNode *init_node = node->data.fn_call_expr.params.at(2);
|
|
|
|
return ir_gen_union_init_expr(irb, scope, node, union_type_inst, name_inst, init_node,
|
|
lval, result_loc);
|
|
}
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval,
|
|
ResultLoc *result_loc)
|
|
{
|
|
assert(node->type == NodeTypeFnCallExpr);
|
|
|
|
if (node->data.fn_call_expr.is_builtin)
|
|
return ir_gen_builtin_fn_call(irb, scope, node, lval, result_loc);
|
|
|
|
AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr;
|
|
IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope);
|
|
if (fn_ref == irb->codegen->invalid_instruction)
|
|
return fn_ref;
|
|
|
|
size_t arg_count = node->data.fn_call_expr.params.length;
|
|
IrInstruction **args = allocate<IrInstruction*>(arg_count);
|
|
for (size_t i = 0; i < arg_count; i += 1) {
|
|
AstNode *arg_node = node->data.fn_call_expr.params.at(i);
|
|
args[i] = ir_gen_node(irb, arg_node, scope);
|
|
if (args[i] == irb->codegen->invalid_instruction)
|
|
return args[i];
|
|
}
|
|
|
|
bool is_async = node->data.fn_call_expr.is_async;
|
|
IrInstruction *fn_call = ir_build_call_src(irb, scope, node, nullptr, fn_ref, arg_count, args, false,
|
|
FnInlineAuto, is_async, nullptr, result_loc);
|
|
return ir_lval_wrap(irb, scope, fn_call, lval, result_loc);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval,
|
|
ResultLoc *result_loc)
|
|
{
|
|
assert(node->type == NodeTypeIfBoolExpr);
|
|
|
|
IrInstruction *condition = ir_gen_node(irb, node->data.if_bool_expr.condition, scope);
|
|
if (condition == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
IrInstruction *is_comptime;
|
|
if (ir_should_inline(irb->exec, scope)) {
|
|
is_comptime = ir_build_const_bool(irb, scope, node, true);
|
|
} else {
|
|
is_comptime = ir_build_test_comptime(irb, scope, node, condition);
|
|
}
|
|
|
|
AstNode *then_node = node->data.if_bool_expr.then_block;
|
|
AstNode *else_node = node->data.if_bool_expr.else_node;
|
|
|
|
IrBasicBlock *then_block = ir_create_basic_block(irb, scope, "Then");
|
|
IrBasicBlock *else_block = ir_create_basic_block(irb, scope, "Else");
|
|
IrBasicBlock *endif_block = ir_create_basic_block(irb, scope, "EndIf");
|
|
|
|
IrInstruction *cond_br_inst = ir_build_cond_br(irb, scope, node, condition,
|
|
then_block, else_block, is_comptime);
|
|
ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, else_block, endif_block,
|
|
result_loc, is_comptime);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, then_block);
|
|
|
|
Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
|
|
IrInstruction *then_expr_result = ir_gen_node_extra(irb, then_node, subexpr_scope, lval,
|
|
&peer_parent->peers.at(0)->base);
|
|
if (then_expr_result == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
IrBasicBlock *after_then_block = irb->current_basic_block;
|
|
if (!instr_is_unreachable(then_expr_result))
|
|
ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime));
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, else_block);
|
|
IrInstruction *else_expr_result;
|
|
if (else_node) {
|
|
else_expr_result = ir_gen_node_extra(irb, else_node, subexpr_scope, lval, &peer_parent->peers.at(1)->base);
|
|
if (else_expr_result == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
} else {
|
|
else_expr_result = ir_build_const_void(irb, scope, node);
|
|
ir_build_end_expr(irb, scope, node, else_expr_result, &peer_parent->peers.at(1)->base);
|
|
}
|
|
IrBasicBlock *after_else_block = irb->current_basic_block;
|
|
if (!instr_is_unreachable(else_expr_result))
|
|
ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime));
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, endif_block);
|
|
IrInstruction **incoming_values = allocate<IrInstruction *>(2);
|
|
incoming_values[0] = then_expr_result;
|
|
incoming_values[1] = else_expr_result;
|
|
IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
|
|
incoming_blocks[0] = after_then_block;
|
|
incoming_blocks[1] = after_else_block;
|
|
|
|
IrInstruction *phi = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, peer_parent);
|
|
return ir_expr_wrap(irb, scope, phi, result_loc);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_prefix_op_id_lval(IrBuilder *irb, Scope *scope, AstNode *node, IrUnOp op_id, LVal lval) {
|
|
assert(node->type == NodeTypePrefixOpExpr);
|
|
AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
|
|
|
|
IrInstruction *value = ir_gen_node_extra(irb, expr_node, scope, lval, nullptr);
|
|
if (value == irb->codegen->invalid_instruction)
|
|
return value;
|
|
|
|
return ir_build_un_op(irb, scope, node, op_id, value);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_prefix_op_id(IrBuilder *irb, Scope *scope, AstNode *node, IrUnOp op_id) {
|
|
return ir_gen_prefix_op_id_lval(irb, scope, node, op_id, LValNone);
|
|
}
|
|
|
|
static IrInstruction *ir_expr_wrap(IrBuilder *irb, Scope *scope, IrInstruction *inst, ResultLoc *result_loc) {
|
|
ir_build_end_expr(irb, scope, inst->source_node, inst, result_loc);
|
|
return inst;
|
|
}
|
|
|
|
static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval,
|
|
ResultLoc *result_loc)
|
|
{
|
|
// This logic must be kept in sync with
|
|
// [STMT_EXPR_TEST_THING] <--- (search this token)
|
|
if (value == irb->codegen->invalid_instruction ||
|
|
instr_is_unreachable(value) ||
|
|
value->source_node->type == NodeTypeDefer ||
|
|
value->id == IrInstructionIdDeclVarSrc)
|
|
{
|
|
return value;
|
|
}
|
|
|
|
if (lval == LValPtr) {
|
|
// We needed a pointer to a value, but we got a value. So we create
|
|
// an instruction which just makes a pointer of it.
|
|
return ir_build_ref(irb, scope, value->source_node, value, false, false);
|
|
} else if (result_loc != nullptr) {
|
|
return ir_expr_wrap(irb, scope, value, result_loc);
|
|
} else {
|
|
return value;
|
|
}
|
|
|
|
}
|
|
|
|
static PtrLen star_token_to_ptr_len(TokenId token_id) {
|
|
switch (token_id) {
|
|
case TokenIdStar:
|
|
case TokenIdStarStar:
|
|
return PtrLenSingle;
|
|
case TokenIdBracketStarBracket:
|
|
return PtrLenUnknown;
|
|
case TokenIdBracketStarCBracket:
|
|
return PtrLenC;
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
|
|
static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode *node) {
|
|
assert(node->type == NodeTypePointerType);
|
|
PtrLen ptr_len = star_token_to_ptr_len(node->data.pointer_type.star_token->id);
|
|
bool is_const = node->data.pointer_type.is_const;
|
|
bool is_volatile = node->data.pointer_type.is_volatile;
|
|
bool is_allow_zero = node->data.pointer_type.allow_zero_token != nullptr;
|
|
AstNode *expr_node = node->data.pointer_type.op_expr;
|
|
AstNode *align_expr = node->data.pointer_type.align_expr;
|
|
|
|
IrInstruction *align_value;
|
|
if (align_expr != nullptr) {
|
|
align_value = ir_gen_node(irb, align_expr, scope);
|
|
if (align_value == irb->codegen->invalid_instruction)
|
|
return align_value;
|
|
} else {
|
|
align_value = nullptr;
|
|
}
|
|
|
|
IrInstruction *child_type = ir_gen_node(irb, expr_node, scope);
|
|
if (child_type == irb->codegen->invalid_instruction)
|
|
return child_type;
|
|
|
|
uint32_t bit_offset_start = 0;
|
|
if (node->data.pointer_type.bit_offset_start != nullptr) {
|
|
if (!bigint_fits_in_bits(node->data.pointer_type.bit_offset_start, 32, false)) {
|
|
Buf *val_buf = buf_alloc();
|
|
bigint_append_buf(val_buf, node->data.pointer_type.bit_offset_start, 10);
|
|
exec_add_error_node(irb->codegen, irb->exec, node,
|
|
buf_sprintf("value %s too large for u32 bit offset", buf_ptr(val_buf)));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
bit_offset_start = bigint_as_unsigned(node->data.pointer_type.bit_offset_start);
|
|
}
|
|
|
|
uint32_t host_int_bytes = 0;
|
|
if (node->data.pointer_type.host_int_bytes != nullptr) {
|
|
if (!bigint_fits_in_bits(node->data.pointer_type.host_int_bytes, 32, false)) {
|
|
Buf *val_buf = buf_alloc();
|
|
bigint_append_buf(val_buf, node->data.pointer_type.host_int_bytes, 10);
|
|
exec_add_error_node(irb->codegen, irb->exec, node,
|
|
buf_sprintf("value %s too large for u32 byte count", buf_ptr(val_buf)));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
host_int_bytes = bigint_as_unsigned(node->data.pointer_type.host_int_bytes);
|
|
}
|
|
|
|
if (host_int_bytes != 0 && bit_offset_start >= host_int_bytes * 8) {
|
|
exec_add_error_node(irb->codegen, irb->exec, node,
|
|
buf_sprintf("bit offset starts after end of host integer"));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
|
|
return ir_build_ptr_type(irb, scope, node, child_type, is_const, is_volatile,
|
|
ptr_len, align_value, bit_offset_start, host_int_bytes, is_allow_zero);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_catch_unreachable(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
AstNode *expr_node, LVal lval, ResultLoc *result_loc)
|
|
{
|
|
IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr);
|
|
if (err_union_ptr == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
IrInstruction *payload_ptr = ir_build_unwrap_err_payload(irb, scope, source_node, err_union_ptr, true, false);
|
|
if (payload_ptr == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
if (lval == LValPtr)
|
|
return payload_ptr;
|
|
|
|
IrInstruction *load_ptr = ir_build_load_ptr(irb, scope, source_node, payload_ptr);
|
|
return ir_expr_wrap(irb, scope, load_ptr, result_loc);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_bool_not(IrBuilder *irb, Scope *scope, AstNode *node) {
|
|
assert(node->type == NodeTypePrefixOpExpr);
|
|
AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
|
|
|
|
IrInstruction *value = ir_gen_node(irb, expr_node, scope);
|
|
if (value == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
return ir_build_bool_not(irb, scope, node, value);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval,
|
|
ResultLoc *result_loc)
|
|
{
|
|
assert(node->type == NodeTypePrefixOpExpr);
|
|
|
|
PrefixOp prefix_op = node->data.prefix_op_expr.prefix_op;
|
|
|
|
switch (prefix_op) {
|
|
case PrefixOpInvalid:
|
|
zig_unreachable();
|
|
case PrefixOpBoolNot:
|
|
return ir_lval_wrap(irb, scope, ir_gen_bool_not(irb, scope, node), lval, result_loc);
|
|
case PrefixOpBinNot:
|
|
return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpBinNot), lval, result_loc);
|
|
case PrefixOpNegation:
|
|
return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegation), lval, result_loc);
|
|
case PrefixOpNegationWrap:
|
|
return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegationWrap), lval, result_loc);
|
|
case PrefixOpOptional:
|
|
return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpOptional), lval, result_loc);
|
|
case PrefixOpAddrOf: {
|
|
AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
|
|
return ir_lval_wrap(irb, scope, ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr), lval, result_loc);
|
|
}
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
static IrInstruction *ir_gen_union_init_expr(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *union_type, IrInstruction *field_name, AstNode *expr_node,
|
|
LVal lval, ResultLoc *parent_result_loc)
|
|
{
|
|
IrInstruction *container_ptr = ir_build_resolve_result(irb, scope, source_node, parent_result_loc, union_type);
|
|
IrInstruction *field_ptr = ir_build_field_ptr_instruction(irb, scope, source_node, container_ptr,
|
|
field_name, true);
|
|
|
|
ResultLocInstruction *result_loc_inst = allocate<ResultLocInstruction>(1);
|
|
result_loc_inst->base.id = ResultLocIdInstruction;
|
|
result_loc_inst->base.source_instruction = field_ptr;
|
|
ir_ref_instruction(field_ptr, irb->current_basic_block);
|
|
ir_build_reset_result(irb, scope, expr_node, &result_loc_inst->base);
|
|
|
|
IrInstruction *expr_value = ir_gen_node_extra(irb, expr_node, scope, LValNone,
|
|
&result_loc_inst->base);
|
|
if (expr_value == irb->codegen->invalid_instruction)
|
|
return expr_value;
|
|
|
|
IrInstruction *init_union = ir_build_union_init_named_field(irb, scope, source_node, union_type,
|
|
field_name, field_ptr, container_ptr);
|
|
|
|
return ir_lval_wrap(irb, scope, init_union, lval, parent_result_loc);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval,
|
|
ResultLoc *parent_result_loc)
|
|
{
|
|
assert(node->type == NodeTypeContainerInitExpr);
|
|
|
|
AstNodeContainerInitExpr *container_init_expr = &node->data.container_init_expr;
|
|
ContainerInitKind kind = container_init_expr->kind;
|
|
|
|
IrInstruction *container_type = nullptr;
|
|
IrInstruction *elem_type = nullptr;
|
|
if (container_init_expr->type->type == NodeTypeInferredArrayType) {
|
|
elem_type = ir_gen_node(irb, container_init_expr->type->data.inferred_array_type.child_type, scope);
|
|
if (elem_type == irb->codegen->invalid_instruction)
|
|
return elem_type;
|
|
} else {
|
|
container_type = ir_gen_node(irb, container_init_expr->type, scope);
|
|
if (container_type == irb->codegen->invalid_instruction)
|
|
return container_type;
|
|
}
|
|
|
|
switch (kind) {
|
|
case ContainerInitKindStruct: {
|
|
if (elem_type != nullptr) {
|
|
add_node_error(irb->codegen, container_init_expr->type,
|
|
buf_sprintf("initializing array with struct syntax"));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *container_ptr = ir_build_resolve_result(irb, scope, node, parent_result_loc,
|
|
container_type);
|
|
|
|
size_t field_count = container_init_expr->entries.length;
|
|
IrInstructionContainerInitFieldsField *fields = allocate<IrInstructionContainerInitFieldsField>(field_count);
|
|
for (size_t i = 0; i < field_count; i += 1) {
|
|
AstNode *entry_node = container_init_expr->entries.at(i);
|
|
assert(entry_node->type == NodeTypeStructValueField);
|
|
|
|
Buf *name = entry_node->data.struct_val_field.name;
|
|
AstNode *expr_node = entry_node->data.struct_val_field.expr;
|
|
|
|
IrInstruction *field_ptr = ir_build_field_ptr(irb, scope, entry_node, container_ptr, name, true);
|
|
ResultLocInstruction *result_loc_inst = allocate<ResultLocInstruction>(1);
|
|
result_loc_inst->base.id = ResultLocIdInstruction;
|
|
result_loc_inst->base.source_instruction = field_ptr;
|
|
result_loc_inst->base.allow_write_through_const = true;
|
|
ir_ref_instruction(field_ptr, irb->current_basic_block);
|
|
ir_build_reset_result(irb, scope, expr_node, &result_loc_inst->base);
|
|
|
|
IrInstruction *expr_value = ir_gen_node_extra(irb, expr_node, scope, LValNone,
|
|
&result_loc_inst->base);
|
|
if (expr_value == irb->codegen->invalid_instruction)
|
|
return expr_value;
|
|
|
|
fields[i].name = name;
|
|
fields[i].source_node = entry_node;
|
|
fields[i].result_loc = field_ptr;
|
|
}
|
|
IrInstruction *init_fields = ir_build_container_init_fields(irb, scope, node, container_type,
|
|
field_count, fields, container_ptr);
|
|
|
|
return ir_lval_wrap(irb, scope, init_fields, lval, parent_result_loc);
|
|
}
|
|
case ContainerInitKindArray: {
|
|
size_t item_count = container_init_expr->entries.length;
|
|
|
|
if (container_type == nullptr) {
|
|
IrInstruction *item_count_inst = ir_build_const_usize(irb, scope, node, item_count);
|
|
container_type = ir_build_array_type(irb, scope, node, item_count_inst, elem_type);
|
|
}
|
|
|
|
IrInstruction *container_ptr = ir_build_resolve_result(irb, scope, node, parent_result_loc,
|
|
container_type);
|
|
|
|
IrInstruction **result_locs = allocate<IrInstruction *>(item_count);
|
|
for (size_t i = 0; i < item_count; i += 1) {
|
|
AstNode *expr_node = container_init_expr->entries.at(i);
|
|
|
|
IrInstruction *elem_index = ir_build_const_usize(irb, scope, expr_node, i);
|
|
IrInstruction *elem_ptr = ir_build_elem_ptr(irb, scope, expr_node, container_ptr, elem_index,
|
|
false, PtrLenSingle, container_type);
|
|
ResultLocInstruction *result_loc_inst = allocate<ResultLocInstruction>(1);
|
|
result_loc_inst->base.id = ResultLocIdInstruction;
|
|
result_loc_inst->base.source_instruction = elem_ptr;
|
|
result_loc_inst->base.allow_write_through_const = true;
|
|
ir_ref_instruction(elem_ptr, irb->current_basic_block);
|
|
ir_build_reset_result(irb, scope, expr_node, &result_loc_inst->base);
|
|
|
|
IrInstruction *expr_value = ir_gen_node_extra(irb, expr_node, scope, LValNone,
|
|
&result_loc_inst->base);
|
|
if (expr_value == irb->codegen->invalid_instruction)
|
|
return expr_value;
|
|
|
|
result_locs[i] = elem_ptr;
|
|
}
|
|
IrInstruction *init_list = ir_build_container_init_list(irb, scope, node, container_type,
|
|
item_count, result_locs, container_ptr);
|
|
return ir_lval_wrap(irb, scope, init_list, lval, parent_result_loc);
|
|
}
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
static ResultLocVar *ir_build_var_result_loc(IrBuilder *irb, IrInstruction *alloca, ZigVar *var) {
|
|
ResultLocVar *result_loc_var = allocate<ResultLocVar>(1);
|
|
result_loc_var->base.id = ResultLocIdVar;
|
|
result_loc_var->base.source_instruction = alloca;
|
|
result_loc_var->var = var;
|
|
|
|
ir_build_reset_result(irb, alloca->scope, alloca->source_node, &result_loc_var->base);
|
|
|
|
return result_loc_var;
|
|
}
|
|
|
|
static void build_decl_var_and_init(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigVar *var,
|
|
IrInstruction *init, const char *name_hint, IrInstruction *is_comptime)
|
|
{
|
|
IrInstruction *alloca = ir_build_alloca_src(irb, scope, source_node, nullptr, name_hint, is_comptime);
|
|
ResultLocVar *var_result_loc = ir_build_var_result_loc(irb, alloca, var);
|
|
ir_build_end_expr(irb, scope, source_node, init, &var_result_loc->base);
|
|
ir_build_var_decl_src(irb, scope, source_node, var, nullptr, alloca);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *node) {
|
|
assert(node->type == NodeTypeVariableDeclaration);
|
|
|
|
AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration;
|
|
|
|
if (buf_eql_str(variable_declaration->symbol, "_")) {
|
|
add_node_error(irb->codegen, node, buf_sprintf("`_` is not a declarable symbol"));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
|
|
// Used for the type expr and the align expr
|
|
Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope);
|
|
|
|
IrInstruction *type_instruction;
|
|
if (variable_declaration->type != nullptr) {
|
|
type_instruction = ir_gen_node(irb, variable_declaration->type, comptime_scope);
|
|
if (type_instruction == irb->codegen->invalid_instruction)
|
|
return type_instruction;
|
|
} else {
|
|
type_instruction = nullptr;
|
|
}
|
|
|
|
bool is_shadowable = false;
|
|
bool is_const = variable_declaration->is_const;
|
|
bool is_extern = variable_declaration->is_extern;
|
|
|
|
bool is_comptime_scalar = ir_should_inline(irb->exec, scope) || variable_declaration->is_comptime;
|
|
IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, is_comptime_scalar);
|
|
ZigVar *var = ir_create_var(irb, node, scope, variable_declaration->symbol,
|
|
is_const, is_const, is_shadowable, is_comptime);
|
|
// we detect IrInstructionIdDeclVarSrc in gen_block to make sure the next node
|
|
// is inside var->child_scope
|
|
|
|
if (!is_extern && !variable_declaration->expr) {
|
|
var->var_type = irb->codegen->builtin_types.entry_invalid;
|
|
add_node_error(irb->codegen, node, buf_sprintf("variables must be initialized"));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *align_value = nullptr;
|
|
if (variable_declaration->align_expr != nullptr) {
|
|
align_value = ir_gen_node(irb, variable_declaration->align_expr, comptime_scope);
|
|
if (align_value == irb->codegen->invalid_instruction)
|
|
return align_value;
|
|
}
|
|
|
|
if (variable_declaration->section_expr != nullptr) {
|
|
add_node_error(irb->codegen, variable_declaration->section_expr,
|
|
buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol)));
|
|
}
|
|
|
|
// Parser should ensure that this never happens
|
|
assert(variable_declaration->threadlocal_tok == nullptr);
|
|
|
|
IrInstruction *alloca = ir_build_alloca_src(irb, scope, node, align_value,
|
|
buf_ptr(variable_declaration->symbol), is_comptime);
|
|
|
|
// Create a result location for the initialization expression.
|
|
ResultLocVar *result_loc_var = ir_build_var_result_loc(irb, alloca, var);
|
|
ResultLoc *init_result_loc = (type_instruction == nullptr) ? &result_loc_var->base : nullptr;
|
|
|
|
Scope *init_scope = is_comptime_scalar ?
|
|
create_comptime_scope(irb->codegen, variable_declaration->expr, scope) : scope;
|
|
|
|
// Temporarily set the name of the IrExecutable to the VariableDeclaration
|
|
// so that the struct or enum from the init expression inherits the name.
|
|
Buf *old_exec_name = irb->exec->name;
|
|
irb->exec->name = variable_declaration->symbol;
|
|
IrInstruction *init_value = ir_gen_node_extra(irb, variable_declaration->expr, init_scope,
|
|
LValNone, init_result_loc);
|
|
irb->exec->name = old_exec_name;
|
|
|
|
if (init_value == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
if (type_instruction != nullptr) {
|
|
IrInstruction *implicit_cast = ir_build_implicit_cast(irb, scope, node, type_instruction, init_value,
|
|
&result_loc_var->base);
|
|
ir_build_end_expr(irb, scope, node, implicit_cast, &result_loc_var->base);
|
|
}
|
|
|
|
return ir_build_var_decl_src(irb, scope, node, var, align_value, alloca);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval,
|
|
ResultLoc *result_loc)
|
|
{
|
|
assert(node->type == NodeTypeWhileExpr);
|
|
|
|
AstNode *continue_expr_node = node->data.while_expr.continue_expr;
|
|
AstNode *else_node = node->data.while_expr.else_node;
|
|
|
|
IrBasicBlock *cond_block = ir_create_basic_block(irb, scope, "WhileCond");
|
|
IrBasicBlock *body_block = ir_create_basic_block(irb, scope, "WhileBody");
|
|
IrBasicBlock *continue_block = continue_expr_node ?
|
|
ir_create_basic_block(irb, scope, "WhileContinue") : cond_block;
|
|
IrBasicBlock *end_block = ir_create_basic_block(irb, scope, "WhileEnd");
|
|
IrBasicBlock *else_block = else_node ?
|
|
ir_create_basic_block(irb, scope, "WhileElse") : end_block;
|
|
|
|
IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node,
|
|
ir_should_inline(irb->exec, scope) || node->data.while_expr.is_inline);
|
|
ir_build_br(irb, scope, node, cond_block, is_comptime);
|
|
|
|
Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
|
|
Buf *var_symbol = node->data.while_expr.var_symbol;
|
|
Buf *err_symbol = node->data.while_expr.err_symbol;
|
|
if (err_symbol != nullptr) {
|
|
ir_set_cursor_at_end_and_append_block(irb, cond_block);
|
|
|
|
Scope *payload_scope;
|
|
AstNode *symbol_node = node; // TODO make more accurate
|
|
ZigVar *payload_var;
|
|
if (var_symbol) {
|
|
// TODO make it an error to write to payload variable
|
|
payload_var = ir_create_var(irb, symbol_node, subexpr_scope, var_symbol,
|
|
true, false, false, is_comptime);
|
|
payload_scope = payload_var->child_scope;
|
|
} else {
|
|
payload_scope = subexpr_scope;
|
|
}
|
|
IrInstruction *err_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, subexpr_scope,
|
|
LValPtr, nullptr);
|
|
if (err_val_ptr == irb->codegen->invalid_instruction)
|
|
return err_val_ptr;
|
|
IrInstruction *is_err = ir_build_test_err_src(irb, scope, node->data.while_expr.condition, err_val_ptr,
|
|
true, false);
|
|
IrBasicBlock *after_cond_block = irb->current_basic_block;
|
|
IrInstruction *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node));
|
|
IrInstruction *cond_br_inst;
|
|
if (!instr_is_unreachable(is_err)) {
|
|
cond_br_inst = ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_err,
|
|
else_block, body_block, is_comptime);
|
|
cond_br_inst->is_gen = true;
|
|
} else {
|
|
// for the purposes of the source instruction to ir_build_result_peers
|
|
cond_br_inst = irb->current_basic_block->instruction_list.last();
|
|
}
|
|
|
|
ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, result_loc,
|
|
is_comptime);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, body_block);
|
|
if (var_symbol) {
|
|
IrInstruction *payload_ptr = ir_build_unwrap_err_payload(irb, payload_scope, symbol_node,
|
|
err_val_ptr, false, false);
|
|
IrInstruction *var_ptr = node->data.while_expr.var_is_ptr ?
|
|
ir_build_ref(irb, payload_scope, symbol_node, payload_ptr, true, false) : payload_ptr;
|
|
ir_build_var_decl_src(irb, payload_scope, symbol_node, payload_var, nullptr, var_ptr);
|
|
}
|
|
|
|
ZigList<IrInstruction *> incoming_values = {0};
|
|
ZigList<IrBasicBlock *> incoming_blocks = {0};
|
|
|
|
ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, payload_scope);
|
|
loop_scope->break_block = end_block;
|
|
loop_scope->continue_block = continue_block;
|
|
loop_scope->is_comptime = is_comptime;
|
|
loop_scope->incoming_blocks = &incoming_blocks;
|
|
loop_scope->incoming_values = &incoming_values;
|
|
loop_scope->lval = lval;
|
|
loop_scope->peer_parent = peer_parent;
|
|
|
|
// Note the body block of the loop is not the place that lval and result_loc are used -
|
|
// it's actually in break statements, handled similarly to return statements.
|
|
// That is why we set those values in loop_scope above and not in this ir_gen_node call.
|
|
IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base);
|
|
if (body_result == irb->codegen->invalid_instruction)
|
|
return body_result;
|
|
|
|
if (!instr_is_unreachable(body_result)) {
|
|
ir_mark_gen(ir_build_check_statement_is_void(irb, payload_scope, node->data.while_expr.body, body_result));
|
|
ir_mark_gen(ir_build_br(irb, payload_scope, node, continue_block, is_comptime));
|
|
}
|
|
|
|
if (continue_expr_node) {
|
|
ir_set_cursor_at_end_and_append_block(irb, continue_block);
|
|
IrInstruction *expr_result = ir_gen_node(irb, continue_expr_node, payload_scope);
|
|
if (expr_result == irb->codegen->invalid_instruction)
|
|
return expr_result;
|
|
if (!instr_is_unreachable(expr_result)) {
|
|
ir_mark_gen(ir_build_check_statement_is_void(irb, payload_scope, continue_expr_node, expr_result));
|
|
ir_mark_gen(ir_build_br(irb, payload_scope, node, cond_block, is_comptime));
|
|
}
|
|
}
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, else_block);
|
|
assert(else_node != nullptr);
|
|
|
|
// TODO make it an error to write to error variable
|
|
AstNode *err_symbol_node = else_node; // TODO make more accurate
|
|
ZigVar *err_var = ir_create_var(irb, err_symbol_node, scope, err_symbol,
|
|
true, false, false, is_comptime);
|
|
Scope *err_scope = err_var->child_scope;
|
|
IrInstruction *err_ptr = ir_build_unwrap_err_code(irb, err_scope, err_symbol_node, err_val_ptr);
|
|
ir_build_var_decl_src(irb, err_scope, symbol_node, err_var, nullptr, err_ptr);
|
|
|
|
if (peer_parent->peers.length != 0) {
|
|
peer_parent->peers.last()->next_bb = else_block;
|
|
}
|
|
ResultLocPeer *peer_result = create_peer_result(peer_parent);
|
|
peer_parent->peers.append(peer_result);
|
|
IrInstruction *else_result = ir_gen_node_extra(irb, else_node, err_scope, lval, &peer_result->base);
|
|
if (else_result == irb->codegen->invalid_instruction)
|
|
return else_result;
|
|
if (!instr_is_unreachable(else_result))
|
|
ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime));
|
|
IrBasicBlock *after_else_block = irb->current_basic_block;
|
|
ir_set_cursor_at_end_and_append_block(irb, end_block);
|
|
if (else_result) {
|
|
incoming_blocks.append(after_else_block);
|
|
incoming_values.append(else_result);
|
|
} else {
|
|
incoming_blocks.append(after_cond_block);
|
|
incoming_values.append(void_else_result);
|
|
}
|
|
if (peer_parent->peers.length != 0) {
|
|
peer_parent->peers.last()->next_bb = end_block;
|
|
}
|
|
|
|
IrInstruction *phi = ir_build_phi(irb, scope, node, incoming_blocks.length,
|
|
incoming_blocks.items, incoming_values.items, peer_parent);
|
|
return ir_expr_wrap(irb, scope, phi, result_loc);
|
|
} else if (var_symbol != nullptr) {
|
|
ir_set_cursor_at_end_and_append_block(irb, cond_block);
|
|
Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
|
|
// TODO make it an error to write to payload variable
|
|
AstNode *symbol_node = node; // TODO make more accurate
|
|
|
|
ZigVar *payload_var = ir_create_var(irb, symbol_node, subexpr_scope, var_symbol,
|
|
true, false, false, is_comptime);
|
|
Scope *child_scope = payload_var->child_scope;
|
|
IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, subexpr_scope,
|
|
LValPtr, nullptr);
|
|
if (maybe_val_ptr == irb->codegen->invalid_instruction)
|
|
return maybe_val_ptr;
|
|
IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, maybe_val_ptr);
|
|
IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node->data.while_expr.condition, maybe_val);
|
|
IrBasicBlock *after_cond_block = irb->current_basic_block;
|
|
IrInstruction *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node));
|
|
IrInstruction *cond_br_inst;
|
|
if (!instr_is_unreachable(is_non_null)) {
|
|
cond_br_inst = ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_non_null,
|
|
body_block, else_block, is_comptime);
|
|
cond_br_inst->is_gen = true;
|
|
} else {
|
|
// for the purposes of the source instruction to ir_build_result_peers
|
|
cond_br_inst = irb->current_basic_block->instruction_list.last();
|
|
}
|
|
|
|
ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, result_loc,
|
|
is_comptime);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, body_block);
|
|
IrInstruction *payload_ptr = ir_build_optional_unwrap_ptr(irb, child_scope, symbol_node, maybe_val_ptr, false, false);
|
|
IrInstruction *var_ptr = node->data.while_expr.var_is_ptr ?
|
|
ir_build_ref(irb, child_scope, symbol_node, payload_ptr, true, false) : payload_ptr;
|
|
ir_build_var_decl_src(irb, child_scope, symbol_node, payload_var, nullptr, var_ptr);
|
|
|
|
ZigList<IrInstruction *> incoming_values = {0};
|
|
ZigList<IrBasicBlock *> incoming_blocks = {0};
|
|
|
|
ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, child_scope);
|
|
loop_scope->break_block = end_block;
|
|
loop_scope->continue_block = continue_block;
|
|
loop_scope->is_comptime = is_comptime;
|
|
loop_scope->incoming_blocks = &incoming_blocks;
|
|
loop_scope->incoming_values = &incoming_values;
|
|
loop_scope->lval = lval;
|
|
loop_scope->peer_parent = peer_parent;
|
|
|
|
// Note the body block of the loop is not the place that lval and result_loc are used -
|
|
// it's actually in break statements, handled similarly to return statements.
|
|
// That is why we set those values in loop_scope above and not in this ir_gen_node call.
|
|
IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base);
|
|
if (body_result == irb->codegen->invalid_instruction)
|
|
return body_result;
|
|
|
|
if (!instr_is_unreachable(body_result)) {
|
|
ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.while_expr.body, body_result));
|
|
ir_mark_gen(ir_build_br(irb, child_scope, node, continue_block, is_comptime));
|
|
}
|
|
|
|
if (continue_expr_node) {
|
|
ir_set_cursor_at_end_and_append_block(irb, continue_block);
|
|
IrInstruction *expr_result = ir_gen_node(irb, continue_expr_node, child_scope);
|
|
if (expr_result == irb->codegen->invalid_instruction)
|
|
return expr_result;
|
|
if (!instr_is_unreachable(expr_result)) {
|
|
ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, continue_expr_node, expr_result));
|
|
ir_mark_gen(ir_build_br(irb, child_scope, node, cond_block, is_comptime));
|
|
}
|
|
}
|
|
|
|
IrInstruction *else_result = nullptr;
|
|
if (else_node) {
|
|
ir_set_cursor_at_end_and_append_block(irb, else_block);
|
|
|
|
if (peer_parent->peers.length != 0) {
|
|
peer_parent->peers.last()->next_bb = else_block;
|
|
}
|
|
ResultLocPeer *peer_result = create_peer_result(peer_parent);
|
|
peer_parent->peers.append(peer_result);
|
|
else_result = ir_gen_node_extra(irb, else_node, scope, lval, &peer_result->base);
|
|
if (else_result == irb->codegen->invalid_instruction)
|
|
return else_result;
|
|
if (!instr_is_unreachable(else_result))
|
|
ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime));
|
|
}
|
|
IrBasicBlock *after_else_block = irb->current_basic_block;
|
|
ir_set_cursor_at_end_and_append_block(irb, end_block);
|
|
if (else_result) {
|
|
incoming_blocks.append(after_else_block);
|
|
incoming_values.append(else_result);
|
|
} else {
|
|
incoming_blocks.append(after_cond_block);
|
|
incoming_values.append(void_else_result);
|
|
}
|
|
if (peer_parent->peers.length != 0) {
|
|
peer_parent->peers.last()->next_bb = end_block;
|
|
}
|
|
|
|
IrInstruction *phi = ir_build_phi(irb, scope, node, incoming_blocks.length,
|
|
incoming_blocks.items, incoming_values.items, peer_parent);
|
|
return ir_expr_wrap(irb, scope, phi, result_loc);
|
|
} else {
|
|
ir_set_cursor_at_end_and_append_block(irb, cond_block);
|
|
IrInstruction *cond_val = ir_gen_node(irb, node->data.while_expr.condition, scope);
|
|
if (cond_val == irb->codegen->invalid_instruction)
|
|
return cond_val;
|
|
IrBasicBlock *after_cond_block = irb->current_basic_block;
|
|
IrInstruction *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node));
|
|
IrInstruction *cond_br_inst;
|
|
if (!instr_is_unreachable(cond_val)) {
|
|
cond_br_inst = ir_build_cond_br(irb, scope, node->data.while_expr.condition, cond_val,
|
|
body_block, else_block, is_comptime);
|
|
cond_br_inst->is_gen = true;
|
|
} else {
|
|
// for the purposes of the source instruction to ir_build_result_peers
|
|
cond_br_inst = irb->current_basic_block->instruction_list.last();
|
|
}
|
|
|
|
ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, result_loc,
|
|
is_comptime);
|
|
ir_set_cursor_at_end_and_append_block(irb, body_block);
|
|
|
|
ZigList<IrInstruction *> incoming_values = {0};
|
|
ZigList<IrBasicBlock *> incoming_blocks = {0};
|
|
|
|
Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
|
|
|
|
ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, subexpr_scope);
|
|
loop_scope->break_block = end_block;
|
|
loop_scope->continue_block = continue_block;
|
|
loop_scope->is_comptime = is_comptime;
|
|
loop_scope->incoming_blocks = &incoming_blocks;
|
|
loop_scope->incoming_values = &incoming_values;
|
|
loop_scope->lval = lval;
|
|
loop_scope->peer_parent = peer_parent;
|
|
|
|
// Note the body block of the loop is not the place that lval and result_loc are used -
|
|
// it's actually in break statements, handled similarly to return statements.
|
|
// That is why we set those values in loop_scope above and not in this ir_gen_node call.
|
|
IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base);
|
|
if (body_result == irb->codegen->invalid_instruction)
|
|
return body_result;
|
|
|
|
if (!instr_is_unreachable(body_result)) {
|
|
ir_mark_gen(ir_build_check_statement_is_void(irb, scope, node->data.while_expr.body, body_result));
|
|
ir_mark_gen(ir_build_br(irb, scope, node, continue_block, is_comptime));
|
|
}
|
|
|
|
if (continue_expr_node) {
|
|
ir_set_cursor_at_end_and_append_block(irb, continue_block);
|
|
IrInstruction *expr_result = ir_gen_node(irb, continue_expr_node, subexpr_scope);
|
|
if (expr_result == irb->codegen->invalid_instruction)
|
|
return expr_result;
|
|
if (!instr_is_unreachable(expr_result)) {
|
|
ir_mark_gen(ir_build_check_statement_is_void(irb, scope, continue_expr_node, expr_result));
|
|
ir_mark_gen(ir_build_br(irb, scope, node, cond_block, is_comptime));
|
|
}
|
|
}
|
|
|
|
IrInstruction *else_result = nullptr;
|
|
if (else_node) {
|
|
ir_set_cursor_at_end_and_append_block(irb, else_block);
|
|
|
|
if (peer_parent->peers.length != 0) {
|
|
peer_parent->peers.last()->next_bb = else_block;
|
|
}
|
|
ResultLocPeer *peer_result = create_peer_result(peer_parent);
|
|
peer_parent->peers.append(peer_result);
|
|
|
|
else_result = ir_gen_node_extra(irb, else_node, subexpr_scope, lval, &peer_result->base);
|
|
if (else_result == irb->codegen->invalid_instruction)
|
|
return else_result;
|
|
if (!instr_is_unreachable(else_result))
|
|
ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime));
|
|
}
|
|
IrBasicBlock *after_else_block = irb->current_basic_block;
|
|
ir_set_cursor_at_end_and_append_block(irb, end_block);
|
|
if (else_result) {
|
|
incoming_blocks.append(after_else_block);
|
|
incoming_values.append(else_result);
|
|
} else {
|
|
incoming_blocks.append(after_cond_block);
|
|
incoming_values.append(void_else_result);
|
|
}
|
|
if (peer_parent->peers.length != 0) {
|
|
peer_parent->peers.last()->next_bb = end_block;
|
|
}
|
|
|
|
IrInstruction *phi = ir_build_phi(irb, scope, node, incoming_blocks.length,
|
|
incoming_blocks.items, incoming_values.items, peer_parent);
|
|
return ir_expr_wrap(irb, scope, phi, result_loc);
|
|
}
|
|
}
|
|
|
|
static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNode *node, LVal lval,
|
|
ResultLoc *result_loc)
|
|
{
|
|
assert(node->type == NodeTypeForExpr);
|
|
|
|
AstNode *array_node = node->data.for_expr.array_expr;
|
|
AstNode *elem_node = node->data.for_expr.elem_node;
|
|
AstNode *index_node = node->data.for_expr.index_node;
|
|
AstNode *body_node = node->data.for_expr.body;
|
|
AstNode *else_node = node->data.for_expr.else_node;
|
|
|
|
if (!elem_node) {
|
|
add_node_error(irb->codegen, node, buf_sprintf("for loop expression missing element parameter"));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
assert(elem_node->type == NodeTypeSymbol);
|
|
|
|
IrInstruction *array_val_ptr = ir_gen_node_extra(irb, array_node, parent_scope, LValPtr, nullptr);
|
|
if (array_val_ptr == irb->codegen->invalid_instruction)
|
|
return array_val_ptr;
|
|
|
|
IrInstruction *is_comptime = ir_build_const_bool(irb, parent_scope, node,
|
|
ir_should_inline(irb->exec, parent_scope) || node->data.for_expr.is_inline);
|
|
|
|
AstNode *index_var_source_node;
|
|
ZigVar *index_var;
|
|
const char *index_var_name;
|
|
if (index_node) {
|
|
index_var_source_node = index_node;
|
|
Buf *index_var_name_buf = index_node->data.symbol_expr.symbol;
|
|
index_var = ir_create_var(irb, index_node, parent_scope, index_var_name_buf, true, false, false, is_comptime);
|
|
index_var_name = buf_ptr(index_var_name_buf);
|
|
} else {
|
|
index_var_source_node = node;
|
|
index_var = ir_create_var(irb, node, parent_scope, nullptr, true, false, true, is_comptime);
|
|
index_var_name = "i";
|
|
}
|
|
|
|
IrInstruction *zero = ir_build_const_usize(irb, parent_scope, node, 0);
|
|
build_decl_var_and_init(irb, parent_scope, index_var_source_node, index_var, zero, index_var_name, is_comptime);
|
|
parent_scope = index_var->child_scope;
|
|
|
|
IrInstruction *one = ir_build_const_usize(irb, parent_scope, node, 1);
|
|
IrInstruction *index_ptr = ir_build_var_ptr(irb, parent_scope, node, index_var);
|
|
|
|
|
|
IrBasicBlock *cond_block = ir_create_basic_block(irb, parent_scope, "ForCond");
|
|
IrBasicBlock *body_block = ir_create_basic_block(irb, parent_scope, "ForBody");
|
|
IrBasicBlock *end_block = ir_create_basic_block(irb, parent_scope, "ForEnd");
|
|
IrBasicBlock *else_block = else_node ? ir_create_basic_block(irb, parent_scope, "ForElse") : end_block;
|
|
IrBasicBlock *continue_block = ir_create_basic_block(irb, parent_scope, "ForContinue");
|
|
|
|
Buf *len_field_name = buf_create_from_str("len");
|
|
IrInstruction *len_ref = ir_build_field_ptr(irb, parent_scope, node, array_val_ptr, len_field_name, false);
|
|
IrInstruction *len_val = ir_build_load_ptr(irb, parent_scope, node, len_ref);
|
|
ir_build_br(irb, parent_scope, node, cond_block, is_comptime);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, cond_block);
|
|
IrInstruction *index_val = ir_build_load_ptr(irb, parent_scope, node, index_ptr);
|
|
IrInstruction *cond = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpLessThan, index_val, len_val, false);
|
|
IrBasicBlock *after_cond_block = irb->current_basic_block;
|
|
IrInstruction *void_else_value = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, parent_scope, node));
|
|
IrInstruction *cond_br_inst = ir_mark_gen(ir_build_cond_br(irb, parent_scope, node, cond,
|
|
body_block, else_block, is_comptime));
|
|
|
|
ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, result_loc, is_comptime);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, body_block);
|
|
IrInstruction *elem_ptr = ir_build_elem_ptr(irb, parent_scope, node, array_val_ptr, index_val, false,
|
|
PtrLenSingle, nullptr);
|
|
// TODO make it an error to write to element variable or i variable.
|
|
Buf *elem_var_name = elem_node->data.symbol_expr.symbol;
|
|
ZigVar *elem_var = ir_create_var(irb, elem_node, parent_scope, elem_var_name, true, false, false, is_comptime);
|
|
Scope *child_scope = elem_var->child_scope;
|
|
|
|
IrInstruction *var_ptr = node->data.for_expr.elem_is_ptr ?
|
|
ir_build_ref(irb, parent_scope, elem_node, elem_ptr, true, false) : elem_ptr;
|
|
ir_build_var_decl_src(irb, parent_scope, elem_node, elem_var, nullptr, var_ptr);
|
|
|
|
ZigList<IrInstruction *> incoming_values = {0};
|
|
ZigList<IrBasicBlock *> incoming_blocks = {0};
|
|
ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, child_scope);
|
|
loop_scope->break_block = end_block;
|
|
loop_scope->continue_block = continue_block;
|
|
loop_scope->is_comptime = is_comptime;
|
|
loop_scope->incoming_blocks = &incoming_blocks;
|
|
loop_scope->incoming_values = &incoming_values;
|
|
loop_scope->lval = LValNone;
|
|
loop_scope->peer_parent = peer_parent;
|
|
|
|
// Note the body block of the loop is not the place that lval and result_loc are used -
|
|
// it's actually in break statements, handled similarly to return statements.
|
|
// That is why we set those values in loop_scope above and not in this ir_gen_node call.
|
|
IrInstruction *body_result = ir_gen_node(irb, body_node, &loop_scope->base);
|
|
|
|
if (!instr_is_unreachable(body_result)) {
|
|
ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.for_expr.body, body_result));
|
|
ir_mark_gen(ir_build_br(irb, child_scope, node, continue_block, is_comptime));
|
|
}
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, continue_block);
|
|
IrInstruction *new_index_val = ir_build_bin_op(irb, child_scope, node, IrBinOpAdd, index_val, one, false);
|
|
ir_build_store_ptr(irb, child_scope, node, index_ptr, new_index_val)->allow_write_through_const = true;
|
|
ir_build_br(irb, child_scope, node, cond_block, is_comptime);
|
|
|
|
IrInstruction *else_result = nullptr;
|
|
if (else_node) {
|
|
ir_set_cursor_at_end_and_append_block(irb, else_block);
|
|
|
|
if (peer_parent->peers.length != 0) {
|
|
peer_parent->peers.last()->next_bb = else_block;
|
|
}
|
|
ResultLocPeer *peer_result = create_peer_result(peer_parent);
|
|
peer_parent->peers.append(peer_result);
|
|
else_result = ir_gen_node_extra(irb, else_node, parent_scope, LValNone, &peer_result->base);
|
|
if (else_result == irb->codegen->invalid_instruction)
|
|
return else_result;
|
|
if (!instr_is_unreachable(else_result))
|
|
ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime));
|
|
}
|
|
IrBasicBlock *after_else_block = irb->current_basic_block;
|
|
ir_set_cursor_at_end_and_append_block(irb, end_block);
|
|
|
|
if (else_result) {
|
|
incoming_blocks.append(after_else_block);
|
|
incoming_values.append(else_result);
|
|
} else {
|
|
incoming_blocks.append(after_cond_block);
|
|
incoming_values.append(void_else_value);
|
|
}
|
|
if (peer_parent->peers.length != 0) {
|
|
peer_parent->peers.last()->next_bb = end_block;
|
|
}
|
|
|
|
IrInstruction *phi = ir_build_phi(irb, parent_scope, node, incoming_blocks.length,
|
|
incoming_blocks.items, incoming_values.items, peer_parent);
|
|
return ir_lval_wrap(irb, parent_scope, phi, lval, result_loc);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_bool_literal(IrBuilder *irb, Scope *scope, AstNode *node) {
|
|
assert(node->type == NodeTypeBoolLiteral);
|
|
return ir_build_const_bool(irb, scope, node, node->data.bool_literal.value);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_enum_literal(IrBuilder *irb, Scope *scope, AstNode *node) {
|
|
assert(node->type == NodeTypeEnumLiteral);
|
|
Buf *name = &node->data.enum_literal.identifier->data.str_lit.str;
|
|
return ir_build_const_enum_literal(irb, scope, node, name);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_string_literal(IrBuilder *irb, Scope *scope, AstNode *node) {
|
|
assert(node->type == NodeTypeStringLiteral);
|
|
|
|
if (node->data.string_literal.c) {
|
|
return ir_build_const_c_str_lit(irb, scope, node, node->data.string_literal.buf);
|
|
} else {
|
|
return ir_build_const_str_lit(irb, scope, node, node->data.string_literal.buf);
|
|
}
|
|
}
|
|
|
|
static IrInstruction *ir_gen_array_type(IrBuilder *irb, Scope *scope, AstNode *node) {
|
|
assert(node->type == NodeTypeArrayType);
|
|
|
|
AstNode *size_node = node->data.array_type.size;
|
|
AstNode *child_type_node = node->data.array_type.child_type;
|
|
bool is_const = node->data.array_type.is_const;
|
|
bool is_volatile = node->data.array_type.is_volatile;
|
|
bool is_allow_zero = node->data.array_type.allow_zero_token != nullptr;
|
|
AstNode *align_expr = node->data.array_type.align_expr;
|
|
|
|
Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope);
|
|
if (size_node) {
|
|
if (is_const) {
|
|
add_node_error(irb->codegen, node, buf_create_from_str("const qualifier invalid on array type"));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
if (is_volatile) {
|
|
add_node_error(irb->codegen, node, buf_create_from_str("volatile qualifier invalid on array type"));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
if (is_allow_zero) {
|
|
add_node_error(irb->codegen, node, buf_create_from_str("allowzero qualifier invalid on array type"));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
if (align_expr != nullptr) {
|
|
add_node_error(irb->codegen, node, buf_create_from_str("align qualifier invalid on array type"));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *size_value = ir_gen_node(irb, size_node, comptime_scope);
|
|
if (size_value == irb->codegen->invalid_instruction)
|
|
return size_value;
|
|
|
|
IrInstruction *child_type = ir_gen_node(irb, child_type_node, comptime_scope);
|
|
if (child_type == irb->codegen->invalid_instruction)
|
|
return child_type;
|
|
|
|
return ir_build_array_type(irb, scope, node, size_value, child_type);
|
|
} else {
|
|
IrInstruction *align_value;
|
|
if (align_expr != nullptr) {
|
|
align_value = ir_gen_node(irb, align_expr, comptime_scope);
|
|
if (align_value == irb->codegen->invalid_instruction)
|
|
return align_value;
|
|
} else {
|
|
align_value = nullptr;
|
|
}
|
|
|
|
IrInstruction *child_type = ir_gen_node(irb, child_type_node, comptime_scope);
|
|
if (child_type == irb->codegen->invalid_instruction)
|
|
return child_type;
|
|
|
|
return ir_build_slice_type(irb, scope, node, child_type, is_const, is_volatile, align_value, is_allow_zero);
|
|
}
|
|
}
|
|
|
|
static IrInstruction *ir_gen_anyframe_type(IrBuilder *irb, Scope *scope, AstNode *node) {
|
|
assert(node->type == NodeTypeAnyFrameType);
|
|
|
|
AstNode *payload_type_node = node->data.anyframe_type.payload_type;
|
|
IrInstruction *payload_type_value = nullptr;
|
|
|
|
if (payload_type_node != nullptr) {
|
|
payload_type_value = ir_gen_node(irb, payload_type_node, scope);
|
|
if (payload_type_value == irb->codegen->invalid_instruction)
|
|
return payload_type_value;
|
|
|
|
}
|
|
|
|
return ir_build_anyframe_type(irb, scope, node, payload_type_value);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_undefined_literal(IrBuilder *irb, Scope *scope, AstNode *node) {
|
|
assert(node->type == NodeTypeUndefinedLiteral);
|
|
return ir_build_const_undefined(irb, scope, node);
|
|
}
|
|
|
|
static Error parse_asm_template(IrBuilder *irb, AstNode *source_node, Buf *asm_template,
|
|
ZigList<AsmToken> *tok_list)
|
|
{
|
|
// TODO Connect the errors in this function back up to the actual source location
|
|
// rather than just the token. https://github.com/ziglang/zig/issues/2080
|
|
enum State {
|
|
StateStart,
|
|
StatePercent,
|
|
StateTemplate,
|
|
StateVar,
|
|
};
|
|
|
|
assert(tok_list->length == 0);
|
|
|
|
AsmToken *cur_tok = nullptr;
|
|
|
|
enum State state = StateStart;
|
|
|
|
for (size_t i = 0; i < buf_len(asm_template); i += 1) {
|
|
uint8_t c = *((uint8_t*)buf_ptr(asm_template) + i);
|
|
switch (state) {
|
|
case StateStart:
|
|
if (c == '%') {
|
|
tok_list->add_one();
|
|
cur_tok = &tok_list->last();
|
|
cur_tok->id = AsmTokenIdPercent;
|
|
cur_tok->start = i;
|
|
state = StatePercent;
|
|
} else {
|
|
tok_list->add_one();
|
|
cur_tok = &tok_list->last();
|
|
cur_tok->id = AsmTokenIdTemplate;
|
|
cur_tok->start = i;
|
|
state = StateTemplate;
|
|
}
|
|
break;
|
|
case StatePercent:
|
|
if (c == '%') {
|
|
cur_tok->end = i;
|
|
state = StateStart;
|
|
} else if (c == '[') {
|
|
cur_tok->id = AsmTokenIdVar;
|
|
state = StateVar;
|
|
} else if (c == '=') {
|
|
cur_tok->id = AsmTokenIdUniqueId;
|
|
cur_tok->end = i;
|
|
state = StateStart;
|
|
} else {
|
|
add_node_error(irb->codegen, source_node,
|
|
buf_create_from_str("expected a '%' or '['"));
|
|
return ErrorSemanticAnalyzeFail;
|
|
}
|
|
break;
|
|
case StateTemplate:
|
|
if (c == '%') {
|
|
cur_tok->end = i;
|
|
i -= 1;
|
|
cur_tok = nullptr;
|
|
state = StateStart;
|
|
}
|
|
break;
|
|
case StateVar:
|
|
if (c == ']') {
|
|
cur_tok->end = i;
|
|
state = StateStart;
|
|
} else if ((c >= 'a' && c <= 'z') ||
|
|
(c >= '0' && c <= '9') ||
|
|
(c == '_'))
|
|
{
|
|
// do nothing
|
|
} else {
|
|
add_node_error(irb->codegen, source_node,
|
|
buf_sprintf("invalid substitution character: '%c'", c));
|
|
return ErrorSemanticAnalyzeFail;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch (state) {
|
|
case StateStart:
|
|
break;
|
|
case StatePercent:
|
|
case StateVar:
|
|
add_node_error(irb->codegen, source_node, buf_sprintf("unexpected end of assembly template"));
|
|
return ErrorSemanticAnalyzeFail;
|
|
case StateTemplate:
|
|
cur_tok->end = buf_len(asm_template);
|
|
break;
|
|
}
|
|
return ErrorNone;
|
|
}
|
|
|
|
static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
|
|
Error err;
|
|
assert(node->type == NodeTypeAsmExpr);
|
|
AstNodeAsmExpr *asm_expr = &node->data.asm_expr;
|
|
bool is_volatile = asm_expr->volatile_token != nullptr;
|
|
bool in_fn_scope = (scope_fn_entry(scope) != nullptr);
|
|
|
|
Buf *template_buf = &asm_expr->asm_template->data.str_lit.str;
|
|
|
|
if (!in_fn_scope) {
|
|
if (is_volatile) {
|
|
add_token_error(irb->codegen, node->owner, asm_expr->volatile_token,
|
|
buf_sprintf("volatile is meaningless on global assembly"));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (asm_expr->output_list.length != 0 || asm_expr->input_list.length != 0 ||
|
|
asm_expr->clobber_list.length != 0)
|
|
{
|
|
add_node_error(irb->codegen, node,
|
|
buf_sprintf("global assembly cannot have inputs, outputs, or clobbers"));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
|
|
return ir_build_global_asm(irb, scope, node, template_buf);
|
|
}
|
|
|
|
ZigList<AsmToken> tok_list = {};
|
|
if ((err = parse_asm_template(irb, node, template_buf, &tok_list))) {
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction **input_list = allocate<IrInstruction *>(asm_expr->input_list.length);
|
|
IrInstruction **output_types = allocate<IrInstruction *>(asm_expr->output_list.length);
|
|
ZigVar **output_vars = allocate<ZigVar *>(asm_expr->output_list.length);
|
|
size_t return_count = 0;
|
|
if (!is_volatile && asm_expr->output_list.length == 0) {
|
|
add_node_error(irb->codegen, node,
|
|
buf_sprintf("assembly expression with no output must be marked volatile"));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
for (size_t i = 0; i < asm_expr->output_list.length; i += 1) {
|
|
AsmOutput *asm_output = asm_expr->output_list.at(i);
|
|
if (asm_output->return_type) {
|
|
return_count += 1;
|
|
|
|
IrInstruction *return_type = ir_gen_node(irb, asm_output->return_type, scope);
|
|
if (return_type == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
if (return_count > 1) {
|
|
add_node_error(irb->codegen, node,
|
|
buf_sprintf("inline assembly allows up to one output value"));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
output_types[i] = return_type;
|
|
} else {
|
|
Buf *variable_name = asm_output->variable_name;
|
|
// TODO there is some duplication here with ir_gen_symbol. I need to do a full audit of how
|
|
// inline assembly works. https://github.com/ziglang/zig/issues/215
|
|
ZigVar *var = find_variable(irb->codegen, scope, variable_name, nullptr);
|
|
if (var) {
|
|
output_vars[i] = var;
|
|
} else {
|
|
add_node_error(irb->codegen, node,
|
|
buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name)));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
|
|
const char modifier = *buf_ptr(asm_output->constraint);
|
|
if (modifier != '=') {
|
|
add_node_error(irb->codegen, node,
|
|
buf_sprintf("invalid modifier starting output constraint for '%s': '%c', only '=' is supported."
|
|
" Compiler TODO: see https://github.com/ziglang/zig/issues/215",
|
|
buf_ptr(asm_output->asm_symbolic_name), modifier));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
for (size_t i = 0; i < asm_expr->input_list.length; i += 1) {
|
|
AsmInput *asm_input = asm_expr->input_list.at(i);
|
|
IrInstruction *input_value = ir_gen_node(irb, asm_input->expr, scope);
|
|
if (input_value == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
input_list[i] = input_value;
|
|
}
|
|
|
|
return ir_build_asm(irb, scope, node, template_buf, tok_list.items, tok_list.length,
|
|
input_list, output_types, output_vars, return_count, is_volatile);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval,
|
|
ResultLoc *result_loc)
|
|
{
|
|
assert(node->type == NodeTypeIfOptional);
|
|
|
|
Buf *var_symbol = node->data.test_expr.var_symbol;
|
|
AstNode *expr_node = node->data.test_expr.target_node;
|
|
AstNode *then_node = node->data.test_expr.then_node;
|
|
AstNode *else_node = node->data.test_expr.else_node;
|
|
bool var_is_ptr = node->data.test_expr.var_is_ptr;
|
|
|
|
IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr);
|
|
if (maybe_val_ptr == irb->codegen->invalid_instruction)
|
|
return maybe_val_ptr;
|
|
|
|
IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node, maybe_val_ptr);
|
|
IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node, maybe_val);
|
|
|
|
IrBasicBlock *then_block = ir_create_basic_block(irb, scope, "OptionalThen");
|
|
IrBasicBlock *else_block = ir_create_basic_block(irb, scope, "OptionalElse");
|
|
IrBasicBlock *endif_block = ir_create_basic_block(irb, scope, "OptionalEndIf");
|
|
|
|
IrInstruction *is_comptime;
|
|
if (ir_should_inline(irb->exec, scope)) {
|
|
is_comptime = ir_build_const_bool(irb, scope, node, true);
|
|
} else {
|
|
is_comptime = ir_build_test_comptime(irb, scope, node, is_non_null);
|
|
}
|
|
IrInstruction *cond_br_inst = ir_build_cond_br(irb, scope, node, is_non_null,
|
|
then_block, else_block, is_comptime);
|
|
|
|
ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, else_block, endif_block,
|
|
result_loc, is_comptime);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, then_block);
|
|
|
|
Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
|
|
Scope *var_scope;
|
|
if (var_symbol) {
|
|
bool is_shadowable = false;
|
|
bool is_const = true;
|
|
ZigVar *var = ir_create_var(irb, node, subexpr_scope,
|
|
var_symbol, is_const, is_const, is_shadowable, is_comptime);
|
|
|
|
IrInstruction *payload_ptr = ir_build_optional_unwrap_ptr(irb, subexpr_scope, node, maybe_val_ptr, false, false);
|
|
IrInstruction *var_ptr = var_is_ptr ? ir_build_ref(irb, subexpr_scope, node, payload_ptr, true, false) : payload_ptr;
|
|
ir_build_var_decl_src(irb, subexpr_scope, node, var, nullptr, var_ptr);
|
|
var_scope = var->child_scope;
|
|
} else {
|
|
var_scope = subexpr_scope;
|
|
}
|
|
IrInstruction *then_expr_result = ir_gen_node_extra(irb, then_node, var_scope, lval,
|
|
&peer_parent->peers.at(0)->base);
|
|
if (then_expr_result == irb->codegen->invalid_instruction)
|
|
return then_expr_result;
|
|
IrBasicBlock *after_then_block = irb->current_basic_block;
|
|
if (!instr_is_unreachable(then_expr_result))
|
|
ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime));
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, else_block);
|
|
IrInstruction *else_expr_result;
|
|
if (else_node) {
|
|
else_expr_result = ir_gen_node_extra(irb, else_node, subexpr_scope, lval, &peer_parent->peers.at(1)->base);
|
|
if (else_expr_result == irb->codegen->invalid_instruction)
|
|
return else_expr_result;
|
|
} else {
|
|
else_expr_result = ir_build_const_void(irb, scope, node);
|
|
ir_build_end_expr(irb, scope, node, else_expr_result, &peer_parent->peers.at(1)->base);
|
|
}
|
|
IrBasicBlock *after_else_block = irb->current_basic_block;
|
|
if (!instr_is_unreachable(else_expr_result))
|
|
ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime));
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, endif_block);
|
|
IrInstruction **incoming_values = allocate<IrInstruction *>(2);
|
|
incoming_values[0] = then_expr_result;
|
|
incoming_values[1] = else_expr_result;
|
|
IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
|
|
incoming_blocks[0] = after_then_block;
|
|
incoming_blocks[1] = after_else_block;
|
|
|
|
IrInstruction *phi = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, peer_parent);
|
|
return ir_expr_wrap(irb, scope, phi, result_loc);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval,
|
|
ResultLoc *result_loc)
|
|
{
|
|
assert(node->type == NodeTypeIfErrorExpr);
|
|
|
|
AstNode *target_node = node->data.if_err_expr.target_node;
|
|
AstNode *then_node = node->data.if_err_expr.then_node;
|
|
AstNode *else_node = node->data.if_err_expr.else_node;
|
|
bool var_is_ptr = node->data.if_err_expr.var_is_ptr;
|
|
bool var_is_const = true;
|
|
Buf *var_symbol = node->data.if_err_expr.var_symbol;
|
|
Buf *err_symbol = node->data.if_err_expr.err_symbol;
|
|
|
|
IrInstruction *err_val_ptr = ir_gen_node_extra(irb, target_node, scope, LValPtr, nullptr);
|
|
if (err_val_ptr == irb->codegen->invalid_instruction)
|
|
return err_val_ptr;
|
|
|
|
IrInstruction *err_val = ir_build_load_ptr(irb, scope, node, err_val_ptr);
|
|
IrInstruction *is_err = ir_build_test_err_src(irb, scope, node, err_val_ptr, true, false);
|
|
|
|
IrBasicBlock *ok_block = ir_create_basic_block(irb, scope, "TryOk");
|
|
IrBasicBlock *else_block = ir_create_basic_block(irb, scope, "TryElse");
|
|
IrBasicBlock *endif_block = ir_create_basic_block(irb, scope, "TryEnd");
|
|
|
|
bool force_comptime = ir_should_inline(irb->exec, scope);
|
|
IrInstruction *is_comptime = force_comptime ? ir_build_const_bool(irb, scope, node, true) : ir_build_test_comptime(irb, scope, node, is_err);
|
|
IrInstruction *cond_br_inst = ir_build_cond_br(irb, scope, node, is_err, else_block, ok_block, is_comptime);
|
|
|
|
ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, else_block, endif_block,
|
|
result_loc, is_comptime);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, ok_block);
|
|
|
|
Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
|
|
Scope *var_scope;
|
|
if (var_symbol) {
|
|
bool is_shadowable = false;
|
|
IrInstruction *var_is_comptime = force_comptime ? ir_build_const_bool(irb, subexpr_scope, node, true) : ir_build_test_comptime(irb, subexpr_scope, node, err_val);
|
|
ZigVar *var = ir_create_var(irb, node, subexpr_scope,
|
|
var_symbol, var_is_const, var_is_const, is_shadowable, var_is_comptime);
|
|
|
|
IrInstruction *payload_ptr = ir_build_unwrap_err_payload(irb, subexpr_scope, node, err_val_ptr, false, false);
|
|
IrInstruction *var_ptr = var_is_ptr ?
|
|
ir_build_ref(irb, subexpr_scope, node, payload_ptr, true, false) : payload_ptr;
|
|
ir_build_var_decl_src(irb, subexpr_scope, node, var, nullptr, var_ptr);
|
|
var_scope = var->child_scope;
|
|
} else {
|
|
var_scope = subexpr_scope;
|
|
}
|
|
IrInstruction *then_expr_result = ir_gen_node_extra(irb, then_node, var_scope, lval,
|
|
&peer_parent->peers.at(0)->base);
|
|
if (then_expr_result == irb->codegen->invalid_instruction)
|
|
return then_expr_result;
|
|
IrBasicBlock *after_then_block = irb->current_basic_block;
|
|
if (!instr_is_unreachable(then_expr_result))
|
|
ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime));
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, else_block);
|
|
|
|
IrInstruction *else_expr_result;
|
|
if (else_node) {
|
|
Scope *err_var_scope;
|
|
if (err_symbol) {
|
|
bool is_shadowable = false;
|
|
bool is_const = true;
|
|
ZigVar *var = ir_create_var(irb, node, subexpr_scope,
|
|
err_symbol, is_const, is_const, is_shadowable, is_comptime);
|
|
|
|
IrInstruction *err_ptr = ir_build_unwrap_err_code(irb, subexpr_scope, node, err_val_ptr);
|
|
ir_build_var_decl_src(irb, subexpr_scope, node, var, nullptr, err_ptr);
|
|
err_var_scope = var->child_scope;
|
|
} else {
|
|
err_var_scope = subexpr_scope;
|
|
}
|
|
else_expr_result = ir_gen_node_extra(irb, else_node, err_var_scope, lval, &peer_parent->peers.at(1)->base);
|
|
if (else_expr_result == irb->codegen->invalid_instruction)
|
|
return else_expr_result;
|
|
} else {
|
|
else_expr_result = ir_build_const_void(irb, scope, node);
|
|
ir_build_end_expr(irb, scope, node, else_expr_result, &peer_parent->peers.at(1)->base);
|
|
}
|
|
IrBasicBlock *after_else_block = irb->current_basic_block;
|
|
if (!instr_is_unreachable(else_expr_result))
|
|
ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime));
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, endif_block);
|
|
IrInstruction **incoming_values = allocate<IrInstruction *>(2);
|
|
incoming_values[0] = then_expr_result;
|
|
incoming_values[1] = else_expr_result;
|
|
IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
|
|
incoming_blocks[0] = after_then_block;
|
|
incoming_blocks[1] = after_else_block;
|
|
|
|
IrInstruction *phi = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, peer_parent);
|
|
return ir_expr_wrap(irb, scope, phi, result_loc);
|
|
}
|
|
|
|
static bool ir_gen_switch_prong_expr(IrBuilder *irb, Scope *scope, AstNode *switch_node, AstNode *prong_node,
|
|
IrBasicBlock *end_block, IrInstruction *is_comptime, IrInstruction *var_is_comptime,
|
|
IrInstruction *target_value_ptr, IrInstruction **prong_values, size_t prong_values_len,
|
|
ZigList<IrBasicBlock *> *incoming_blocks, ZigList<IrInstruction *> *incoming_values,
|
|
IrInstructionSwitchElseVar **out_switch_else_var, LVal lval, ResultLoc *result_loc)
|
|
{
|
|
assert(switch_node->type == NodeTypeSwitchExpr);
|
|
assert(prong_node->type == NodeTypeSwitchProng);
|
|
|
|
AstNode *expr_node = prong_node->data.switch_prong.expr;
|
|
AstNode *var_symbol_node = prong_node->data.switch_prong.var_symbol;
|
|
Scope *child_scope;
|
|
if (var_symbol_node) {
|
|
assert(var_symbol_node->type == NodeTypeSymbol);
|
|
Buf *var_name = var_symbol_node->data.symbol_expr.symbol;
|
|
bool var_is_ptr = prong_node->data.switch_prong.var_is_ptr;
|
|
|
|
bool is_shadowable = false;
|
|
bool is_const = true;
|
|
ZigVar *var = ir_create_var(irb, var_symbol_node, scope,
|
|
var_name, is_const, is_const, is_shadowable, var_is_comptime);
|
|
child_scope = var->child_scope;
|
|
IrInstruction *var_ptr;
|
|
if (out_switch_else_var != nullptr) {
|
|
IrInstructionSwitchElseVar *switch_else_var = ir_build_switch_else_var(irb, scope, var_symbol_node,
|
|
target_value_ptr);
|
|
*out_switch_else_var = switch_else_var;
|
|
IrInstruction *payload_ptr = &switch_else_var->base;
|
|
var_ptr = var_is_ptr ? ir_build_ref(irb, scope, var_symbol_node, payload_ptr, true, false) : payload_ptr;
|
|
} else if (prong_values != nullptr) {
|
|
IrInstruction *payload_ptr = ir_build_switch_var(irb, scope, var_symbol_node, target_value_ptr,
|
|
prong_values, prong_values_len);
|
|
var_ptr = var_is_ptr ? ir_build_ref(irb, scope, var_symbol_node, payload_ptr, true, false) : payload_ptr;
|
|
} else {
|
|
var_ptr = var_is_ptr ?
|
|
ir_build_ref(irb, scope, var_symbol_node, target_value_ptr, true, false) : target_value_ptr;
|
|
}
|
|
ir_build_var_decl_src(irb, scope, var_symbol_node, var, nullptr, var_ptr);
|
|
} else {
|
|
child_scope = scope;
|
|
}
|
|
|
|
IrInstruction *expr_result = ir_gen_node_extra(irb, expr_node, child_scope, lval, result_loc);
|
|
if (expr_result == irb->codegen->invalid_instruction)
|
|
return false;
|
|
if (!instr_is_unreachable(expr_result))
|
|
ir_mark_gen(ir_build_br(irb, scope, switch_node, end_block, is_comptime));
|
|
incoming_blocks->append(irb->current_basic_block);
|
|
incoming_values->append(expr_result);
|
|
return true;
|
|
}
|
|
|
|
static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval,
|
|
ResultLoc *result_loc)
|
|
{
|
|
assert(node->type == NodeTypeSwitchExpr);
|
|
|
|
AstNode *target_node = node->data.switch_expr.expr;
|
|
IrInstruction *target_value_ptr = ir_gen_node_extra(irb, target_node, scope, LValPtr, nullptr);
|
|
if (target_value_ptr == irb->codegen->invalid_instruction)
|
|
return target_value_ptr;
|
|
IrInstruction *target_value = ir_build_switch_target(irb, scope, node, target_value_ptr);
|
|
|
|
IrBasicBlock *else_block = ir_create_basic_block(irb, scope, "SwitchElse");
|
|
IrBasicBlock *end_block = ir_create_basic_block(irb, scope, "SwitchEnd");
|
|
|
|
size_t prong_count = node->data.switch_expr.prongs.length;
|
|
ZigList<IrInstructionSwitchBrCase> cases = {0};
|
|
|
|
IrInstruction *is_comptime;
|
|
IrInstruction *var_is_comptime;
|
|
if (ir_should_inline(irb->exec, scope)) {
|
|
is_comptime = ir_build_const_bool(irb, scope, node, true);
|
|
var_is_comptime = is_comptime;
|
|
} else {
|
|
is_comptime = ir_build_test_comptime(irb, scope, node, target_value);
|
|
var_is_comptime = ir_build_test_comptime(irb, scope, node, target_value_ptr);
|
|
}
|
|
|
|
ZigList<IrInstruction *> incoming_values = {0};
|
|
ZigList<IrBasicBlock *> incoming_blocks = {0};
|
|
ZigList<IrInstructionCheckSwitchProngsRange> check_ranges = {0};
|
|
|
|
IrInstructionSwitchElseVar *switch_else_var = nullptr;
|
|
|
|
ResultLocPeerParent *peer_parent = allocate<ResultLocPeerParent>(1);
|
|
peer_parent->base.id = ResultLocIdPeerParent;
|
|
peer_parent->end_bb = end_block;
|
|
peer_parent->is_comptime = is_comptime;
|
|
peer_parent->parent = result_loc;
|
|
|
|
ir_build_reset_result(irb, scope, node, &peer_parent->base);
|
|
|
|
// First do the else and the ranges
|
|
Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
|
|
Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope);
|
|
AstNode *else_prong = nullptr;
|
|
for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) {
|
|
AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i);
|
|
size_t prong_item_count = prong_node->data.switch_prong.items.length;
|
|
if (prong_item_count == 0) {
|
|
ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent);
|
|
if (else_prong) {
|
|
ErrorMsg *msg = add_node_error(irb->codegen, prong_node,
|
|
buf_sprintf("multiple else prongs in switch expression"));
|
|
add_error_note(irb->codegen, msg, else_prong,
|
|
buf_sprintf("previous else prong is here"));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
else_prong = prong_node;
|
|
|
|
IrBasicBlock *prev_block = irb->current_basic_block;
|
|
if (peer_parent->peers.length > 0) {
|
|
peer_parent->peers.last()->next_bb = else_block;
|
|
}
|
|
peer_parent->peers.append(this_peer_result_loc);
|
|
ir_set_cursor_at_end_and_append_block(irb, else_block);
|
|
if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block,
|
|
is_comptime, var_is_comptime, target_value_ptr, nullptr, 0, &incoming_blocks, &incoming_values,
|
|
&switch_else_var, LValNone, &this_peer_result_loc->base))
|
|
{
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
ir_set_cursor_at_end(irb, prev_block);
|
|
} else if (prong_node->data.switch_prong.any_items_are_range) {
|
|
ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent);
|
|
|
|
IrInstruction *ok_bit = nullptr;
|
|
AstNode *last_item_node = nullptr;
|
|
for (size_t item_i = 0; item_i < prong_item_count; item_i += 1) {
|
|
AstNode *item_node = prong_node->data.switch_prong.items.at(item_i);
|
|
last_item_node = item_node;
|
|
if (item_node->type == NodeTypeSwitchRange) {
|
|
AstNode *start_node = item_node->data.switch_range.start;
|
|
AstNode *end_node = item_node->data.switch_range.end;
|
|
|
|
IrInstruction *start_value = ir_gen_node(irb, start_node, comptime_scope);
|
|
if (start_value == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
IrInstruction *end_value = ir_gen_node(irb, end_node, comptime_scope);
|
|
if (end_value == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
IrInstructionCheckSwitchProngsRange *check_range = check_ranges.add_one();
|
|
check_range->start = start_value;
|
|
check_range->end = end_value;
|
|
|
|
IrInstruction *lower_range_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpCmpGreaterOrEq,
|
|
target_value, start_value, false);
|
|
IrInstruction *upper_range_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpCmpLessOrEq,
|
|
target_value, end_value, false);
|
|
IrInstruction *both_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpBoolAnd,
|
|
lower_range_ok, upper_range_ok, false);
|
|
if (ok_bit) {
|
|
ok_bit = ir_build_bin_op(irb, scope, item_node, IrBinOpBoolOr, both_ok, ok_bit, false);
|
|
} else {
|
|
ok_bit = both_ok;
|
|
}
|
|
} else {
|
|
IrInstruction *item_value = ir_gen_node(irb, item_node, comptime_scope);
|
|
if (item_value == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
IrInstructionCheckSwitchProngsRange *check_range = check_ranges.add_one();
|
|
check_range->start = item_value;
|
|
check_range->end = item_value;
|
|
|
|
IrInstruction *cmp_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpCmpEq,
|
|
item_value, target_value, false);
|
|
if (ok_bit) {
|
|
ok_bit = ir_build_bin_op(irb, scope, item_node, IrBinOpBoolOr, cmp_ok, ok_bit, false);
|
|
} else {
|
|
ok_bit = cmp_ok;
|
|
}
|
|
}
|
|
}
|
|
|
|
IrBasicBlock *range_block_yes = ir_create_basic_block(irb, scope, "SwitchRangeYes");
|
|
IrBasicBlock *range_block_no = ir_create_basic_block(irb, scope, "SwitchRangeNo");
|
|
|
|
assert(ok_bit);
|
|
assert(last_item_node);
|
|
IrInstruction *br_inst = ir_mark_gen(ir_build_cond_br(irb, scope, last_item_node, ok_bit,
|
|
range_block_yes, range_block_no, is_comptime));
|
|
if (peer_parent->base.source_instruction == nullptr) {
|
|
peer_parent->base.source_instruction = br_inst;
|
|
}
|
|
|
|
if (peer_parent->peers.length > 0) {
|
|
peer_parent->peers.last()->next_bb = range_block_yes;
|
|
}
|
|
peer_parent->peers.append(this_peer_result_loc);
|
|
ir_set_cursor_at_end_and_append_block(irb, range_block_yes);
|
|
if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block,
|
|
is_comptime, var_is_comptime, target_value_ptr, nullptr, 0,
|
|
&incoming_blocks, &incoming_values, nullptr, LValNone, &this_peer_result_loc->base))
|
|
{
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, range_block_no);
|
|
}
|
|
}
|
|
|
|
// next do the non-else non-ranges
|
|
for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) {
|
|
AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i);
|
|
size_t prong_item_count = prong_node->data.switch_prong.items.length;
|
|
if (prong_item_count == 0)
|
|
continue;
|
|
if (prong_node->data.switch_prong.any_items_are_range)
|
|
continue;
|
|
|
|
ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent);
|
|
|
|
IrBasicBlock *prong_block = ir_create_basic_block(irb, scope, "SwitchProng");
|
|
IrInstruction **items = allocate<IrInstruction *>(prong_item_count);
|
|
|
|
for (size_t item_i = 0; item_i < prong_item_count; item_i += 1) {
|
|
AstNode *item_node = prong_node->data.switch_prong.items.at(item_i);
|
|
assert(item_node->type != NodeTypeSwitchRange);
|
|
|
|
IrInstruction *item_value = ir_gen_node(irb, item_node, comptime_scope);
|
|
if (item_value == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
IrInstructionCheckSwitchProngsRange *check_range = check_ranges.add_one();
|
|
check_range->start = item_value;
|
|
check_range->end = item_value;
|
|
|
|
IrInstructionSwitchBrCase *this_case = cases.add_one();
|
|
this_case->value = item_value;
|
|
this_case->block = prong_block;
|
|
|
|
items[item_i] = item_value;
|
|
}
|
|
|
|
IrBasicBlock *prev_block = irb->current_basic_block;
|
|
if (peer_parent->peers.length > 0) {
|
|
peer_parent->peers.last()->next_bb = prong_block;
|
|
}
|
|
peer_parent->peers.append(this_peer_result_loc);
|
|
ir_set_cursor_at_end_and_append_block(irb, prong_block);
|
|
if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block,
|
|
is_comptime, var_is_comptime, target_value_ptr, items, prong_item_count,
|
|
&incoming_blocks, &incoming_values, nullptr, LValNone, &this_peer_result_loc->base))
|
|
{
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
|
|
ir_set_cursor_at_end(irb, prev_block);
|
|
|
|
}
|
|
|
|
IrInstruction *switch_prongs_void = ir_build_check_switch_prongs(irb, scope, node, target_value,
|
|
check_ranges.items, check_ranges.length, else_prong != nullptr);
|
|
|
|
IrInstruction *br_instruction;
|
|
if (cases.length == 0) {
|
|
br_instruction = ir_build_br(irb, scope, node, else_block, is_comptime);
|
|
} else {
|
|
IrInstructionSwitchBr *switch_br = ir_build_switch_br(irb, scope, node, target_value, else_block,
|
|
cases.length, cases.items, is_comptime, switch_prongs_void);
|
|
if (switch_else_var != nullptr) {
|
|
switch_else_var->switch_br = switch_br;
|
|
}
|
|
br_instruction = &switch_br->base;
|
|
}
|
|
if (peer_parent->base.source_instruction == nullptr) {
|
|
peer_parent->base.source_instruction = br_instruction;
|
|
}
|
|
for (size_t i = 0; i < peer_parent->peers.length; i += 1) {
|
|
peer_parent->peers.at(i)->base.source_instruction = peer_parent->base.source_instruction;
|
|
}
|
|
|
|
if (!else_prong) {
|
|
if (peer_parent->peers.length != 0) {
|
|
peer_parent->peers.last()->next_bb = else_block;
|
|
}
|
|
ir_set_cursor_at_end_and_append_block(irb, else_block);
|
|
ir_build_unreachable(irb, scope, node);
|
|
} else {
|
|
if (peer_parent->peers.length != 0) {
|
|
peer_parent->peers.last()->next_bb = end_block;
|
|
}
|
|
}
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, end_block);
|
|
assert(incoming_blocks.length == incoming_values.length);
|
|
IrInstruction *result_instruction;
|
|
if (incoming_blocks.length == 0) {
|
|
result_instruction = ir_build_const_void(irb, scope, node);
|
|
} else {
|
|
result_instruction = ir_build_phi(irb, scope, node, incoming_blocks.length,
|
|
incoming_blocks.items, incoming_values.items, peer_parent);
|
|
}
|
|
return ir_lval_wrap(irb, scope, result_instruction, lval, result_loc);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_comptime(IrBuilder *irb, Scope *parent_scope, AstNode *node, LVal lval) {
|
|
assert(node->type == NodeTypeCompTime);
|
|
|
|
Scope *child_scope = create_comptime_scope(irb->codegen, node, parent_scope);
|
|
// purposefully pass null for result_loc and let EndExpr handle it
|
|
return ir_gen_node_extra(irb, node->data.comptime_expr.expr, child_scope, lval, nullptr);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_return_from_block(IrBuilder *irb, Scope *break_scope, AstNode *node, ScopeBlock *block_scope) {
|
|
IrInstruction *is_comptime;
|
|
if (ir_should_inline(irb->exec, break_scope)) {
|
|
is_comptime = ir_build_const_bool(irb, break_scope, node, true);
|
|
} else {
|
|
is_comptime = block_scope->is_comptime;
|
|
}
|
|
|
|
IrInstruction *result_value;
|
|
if (node->data.break_expr.expr) {
|
|
ResultLocPeer *peer_result = create_peer_result(block_scope->peer_parent);
|
|
block_scope->peer_parent->peers.append(peer_result);
|
|
|
|
result_value = ir_gen_node_extra(irb, node->data.break_expr.expr, break_scope, block_scope->lval,
|
|
&peer_result->base);
|
|
if (result_value == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
} else {
|
|
result_value = ir_build_const_void(irb, break_scope, node);
|
|
}
|
|
|
|
IrBasicBlock *dest_block = block_scope->end_block;
|
|
ir_gen_defers_for_block(irb, break_scope, dest_block->scope, false);
|
|
|
|
block_scope->incoming_blocks->append(irb->current_basic_block);
|
|
block_scope->incoming_values->append(result_value);
|
|
return ir_build_br(irb, break_scope, node, dest_block, is_comptime);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *node) {
|
|
assert(node->type == NodeTypeBreak);
|
|
|
|
// Search up the scope. We'll find one of these things first:
|
|
// * function definition scope or global scope => error, break outside loop
|
|
// * defer expression scope => error, cannot break out of defer expression
|
|
// * loop scope => OK
|
|
// * (if it's a labeled break) labeled block => OK
|
|
|
|
Scope *search_scope = break_scope;
|
|
ScopeLoop *loop_scope;
|
|
for (;;) {
|
|
if (search_scope == nullptr || search_scope->id == ScopeIdFnDef) {
|
|
if (node->data.break_expr.name != nullptr) {
|
|
add_node_error(irb->codegen, node, buf_sprintf("label not found: '%s'", buf_ptr(node->data.break_expr.name)));
|
|
return irb->codegen->invalid_instruction;
|
|
} else {
|
|
add_node_error(irb->codegen, node, buf_sprintf("break expression outside loop"));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
} else if (search_scope->id == ScopeIdDeferExpr) {
|
|
add_node_error(irb->codegen, node, buf_sprintf("cannot break out of defer expression"));
|
|
return irb->codegen->invalid_instruction;
|
|
} else if (search_scope->id == ScopeIdLoop) {
|
|
ScopeLoop *this_loop_scope = (ScopeLoop *)search_scope;
|
|
if (node->data.break_expr.name == nullptr ||
|
|
(this_loop_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_loop_scope->name)))
|
|
{
|
|
loop_scope = this_loop_scope;
|
|
break;
|
|
}
|
|
} else if (search_scope->id == ScopeIdBlock) {
|
|
ScopeBlock *this_block_scope = (ScopeBlock *)search_scope;
|
|
if (node->data.break_expr.name != nullptr &&
|
|
(this_block_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_block_scope->name)))
|
|
{
|
|
assert(this_block_scope->end_block != nullptr);
|
|
return ir_gen_return_from_block(irb, break_scope, node, this_block_scope);
|
|
}
|
|
} else if (search_scope->id == ScopeIdSuspend) {
|
|
add_node_error(irb->codegen, node, buf_sprintf("cannot break out of suspend block"));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
search_scope = search_scope->parent;
|
|
}
|
|
|
|
IrInstruction *is_comptime;
|
|
if (ir_should_inline(irb->exec, break_scope)) {
|
|
is_comptime = ir_build_const_bool(irb, break_scope, node, true);
|
|
} else {
|
|
is_comptime = loop_scope->is_comptime;
|
|
}
|
|
|
|
IrInstruction *result_value;
|
|
if (node->data.break_expr.expr) {
|
|
ResultLocPeer *peer_result = create_peer_result(loop_scope->peer_parent);
|
|
loop_scope->peer_parent->peers.append(peer_result);
|
|
|
|
result_value = ir_gen_node_extra(irb, node->data.break_expr.expr, break_scope,
|
|
loop_scope->lval, &peer_result->base);
|
|
if (result_value == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
} else {
|
|
result_value = ir_build_const_void(irb, break_scope, node);
|
|
}
|
|
|
|
IrBasicBlock *dest_block = loop_scope->break_block;
|
|
ir_gen_defers_for_block(irb, break_scope, dest_block->scope, false);
|
|
|
|
loop_scope->incoming_blocks->append(irb->current_basic_block);
|
|
loop_scope->incoming_values->append(result_value);
|
|
return ir_build_br(irb, break_scope, node, dest_block, is_comptime);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_continue(IrBuilder *irb, Scope *continue_scope, AstNode *node) {
|
|
assert(node->type == NodeTypeContinue);
|
|
|
|
// Search up the scope. We'll find one of these things first:
|
|
// * function definition scope or global scope => error, break outside loop
|
|
// * defer expression scope => error, cannot break out of defer expression
|
|
// * loop scope => OK
|
|
|
|
ZigList<ScopeRuntime *> runtime_scopes = {};
|
|
|
|
Scope *search_scope = continue_scope;
|
|
ScopeLoop *loop_scope;
|
|
for (;;) {
|
|
if (search_scope == nullptr || search_scope->id == ScopeIdFnDef) {
|
|
if (node->data.continue_expr.name != nullptr) {
|
|
add_node_error(irb->codegen, node, buf_sprintf("labeled loop not found: '%s'", buf_ptr(node->data.continue_expr.name)));
|
|
return irb->codegen->invalid_instruction;
|
|
} else {
|
|
add_node_error(irb->codegen, node, buf_sprintf("continue expression outside loop"));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
} else if (search_scope->id == ScopeIdDeferExpr) {
|
|
add_node_error(irb->codegen, node, buf_sprintf("cannot continue out of defer expression"));
|
|
return irb->codegen->invalid_instruction;
|
|
} else if (search_scope->id == ScopeIdLoop) {
|
|
ScopeLoop *this_loop_scope = (ScopeLoop *)search_scope;
|
|
if (node->data.continue_expr.name == nullptr ||
|
|
(this_loop_scope->name != nullptr && buf_eql_buf(node->data.continue_expr.name, this_loop_scope->name)))
|
|
{
|
|
loop_scope = this_loop_scope;
|
|
break;
|
|
}
|
|
} else if (search_scope->id == ScopeIdRuntime) {
|
|
ScopeRuntime *scope_runtime = (ScopeRuntime *)search_scope;
|
|
runtime_scopes.append(scope_runtime);
|
|
}
|
|
search_scope = search_scope->parent;
|
|
}
|
|
|
|
IrInstruction *is_comptime;
|
|
if (ir_should_inline(irb->exec, continue_scope)) {
|
|
is_comptime = ir_build_const_bool(irb, continue_scope, node, true);
|
|
} else {
|
|
is_comptime = loop_scope->is_comptime;
|
|
}
|
|
|
|
for (size_t i = 0; i < runtime_scopes.length; i += 1) {
|
|
ScopeRuntime *scope_runtime = runtime_scopes.at(i);
|
|
ir_mark_gen(ir_build_check_runtime_scope(irb, continue_scope, node, scope_runtime->is_comptime, is_comptime));
|
|
}
|
|
|
|
IrBasicBlock *dest_block = loop_scope->continue_block;
|
|
ir_gen_defers_for_block(irb, continue_scope, dest_block->scope, false);
|
|
return ir_mark_gen(ir_build_br(irb, continue_scope, node, dest_block, is_comptime));
|
|
}
|
|
|
|
static IrInstruction *ir_gen_error_type(IrBuilder *irb, Scope *scope, AstNode *node) {
|
|
assert(node->type == NodeTypeErrorType);
|
|
return ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_global_error_set);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_defer(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
|
|
assert(node->type == NodeTypeDefer);
|
|
|
|
ScopeDefer *defer_child_scope = create_defer_scope(irb->codegen, node, parent_scope);
|
|
node->data.defer.child_scope = &defer_child_scope->base;
|
|
|
|
ScopeDeferExpr *defer_expr_scope = create_defer_expr_scope(irb->codegen, node, parent_scope);
|
|
node->data.defer.expr_scope = &defer_expr_scope->base;
|
|
|
|
return ir_build_const_void(irb, parent_scope, node);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) {
|
|
assert(node->type == NodeTypeSliceExpr);
|
|
|
|
AstNodeSliceExpr *slice_expr = &node->data.slice_expr;
|
|
AstNode *array_node = slice_expr->array_ref_expr;
|
|
AstNode *start_node = slice_expr->start;
|
|
AstNode *end_node = slice_expr->end;
|
|
|
|
IrInstruction *ptr_value = ir_gen_node_extra(irb, array_node, scope, LValPtr, nullptr);
|
|
if (ptr_value == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
IrInstruction *start_value = ir_gen_node(irb, start_node, scope);
|
|
if (start_value == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
IrInstruction *end_value;
|
|
if (end_node) {
|
|
end_value = ir_gen_node(irb, end_node, scope);
|
|
if (end_value == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
} else {
|
|
end_value = nullptr;
|
|
}
|
|
|
|
IrInstruction *slice = ir_build_slice_src(irb, scope, node, ptr_value, start_value, end_value, true, result_loc);
|
|
return ir_lval_wrap(irb, scope, slice, lval, result_loc);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode *node, LVal lval,
|
|
ResultLoc *result_loc)
|
|
{
|
|
assert(node->type == NodeTypeCatchExpr);
|
|
|
|
AstNode *op1_node = node->data.unwrap_err_expr.op1;
|
|
AstNode *op2_node = node->data.unwrap_err_expr.op2;
|
|
AstNode *var_node = node->data.unwrap_err_expr.symbol;
|
|
|
|
if (op2_node->type == NodeTypeUnreachable) {
|
|
if (var_node != nullptr) {
|
|
assert(var_node->type == NodeTypeSymbol);
|
|
Buf *var_name = var_node->data.symbol_expr.symbol;
|
|
add_node_error(irb->codegen, var_node, buf_sprintf("unused variable: '%s'", buf_ptr(var_name)));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
return ir_gen_catch_unreachable(irb, parent_scope, node, op1_node, lval, result_loc);
|
|
}
|
|
|
|
|
|
IrInstruction *err_union_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LValPtr, nullptr);
|
|
if (err_union_ptr == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
IrInstruction *is_err = ir_build_test_err_src(irb, parent_scope, node, err_union_ptr, true, false);
|
|
|
|
IrInstruction *is_comptime;
|
|
if (ir_should_inline(irb->exec, parent_scope)) {
|
|
is_comptime = ir_build_const_bool(irb, parent_scope, node, true);
|
|
} else {
|
|
is_comptime = ir_build_test_comptime(irb, parent_scope, node, is_err);
|
|
}
|
|
|
|
IrBasicBlock *ok_block = ir_create_basic_block(irb, parent_scope, "UnwrapErrOk");
|
|
IrBasicBlock *err_block = ir_create_basic_block(irb, parent_scope, "UnwrapErrError");
|
|
IrBasicBlock *end_block = ir_create_basic_block(irb, parent_scope, "UnwrapErrEnd");
|
|
IrInstruction *cond_br_inst = ir_build_cond_br(irb, parent_scope, node, is_err, err_block, ok_block, is_comptime);
|
|
|
|
ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, ok_block, end_block, result_loc,
|
|
is_comptime);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, err_block);
|
|
Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, parent_scope, is_comptime);
|
|
Scope *err_scope;
|
|
if (var_node) {
|
|
assert(var_node->type == NodeTypeSymbol);
|
|
Buf *var_name = var_node->data.symbol_expr.symbol;
|
|
bool is_const = true;
|
|
bool is_shadowable = false;
|
|
ZigVar *var = ir_create_var(irb, node, subexpr_scope, var_name,
|
|
is_const, is_const, is_shadowable, is_comptime);
|
|
err_scope = var->child_scope;
|
|
IrInstruction *err_ptr = ir_build_unwrap_err_code(irb, err_scope, node, err_union_ptr);
|
|
ir_build_var_decl_src(irb, err_scope, var_node, var, nullptr, err_ptr);
|
|
} else {
|
|
err_scope = subexpr_scope;
|
|
}
|
|
IrInstruction *err_result = ir_gen_node_extra(irb, op2_node, err_scope, LValNone, &peer_parent->peers.at(0)->base);
|
|
if (err_result == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
IrBasicBlock *after_err_block = irb->current_basic_block;
|
|
if (!instr_is_unreachable(err_result))
|
|
ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime));
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, ok_block);
|
|
IrInstruction *unwrapped_ptr = ir_build_unwrap_err_payload(irb, parent_scope, node, err_union_ptr, false, false);
|
|
IrInstruction *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr);
|
|
ir_build_end_expr(irb, parent_scope, node, unwrapped_payload, &peer_parent->peers.at(1)->base);
|
|
IrBasicBlock *after_ok_block = irb->current_basic_block;
|
|
ir_build_br(irb, parent_scope, node, end_block, is_comptime);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, end_block);
|
|
IrInstruction **incoming_values = allocate<IrInstruction *>(2);
|
|
incoming_values[0] = err_result;
|
|
incoming_values[1] = unwrapped_payload;
|
|
IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
|
|
incoming_blocks[0] = after_err_block;
|
|
incoming_blocks[1] = after_ok_block;
|
|
IrInstruction *phi = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values, peer_parent);
|
|
return ir_lval_wrap(irb, parent_scope, phi, lval, result_loc);
|
|
}
|
|
|
|
static bool render_instance_name_recursive(CodeGen *codegen, Buf *name, Scope *outer_scope, Scope *inner_scope) {
|
|
if (inner_scope == nullptr || inner_scope == outer_scope) return false;
|
|
bool need_comma = render_instance_name_recursive(codegen, name, outer_scope, inner_scope->parent);
|
|
if (inner_scope->id != ScopeIdVarDecl)
|
|
return need_comma;
|
|
|
|
ScopeVarDecl *var_scope = (ScopeVarDecl *)inner_scope;
|
|
if (need_comma)
|
|
buf_append_char(name, ',');
|
|
// TODO: const ptr reinterpret here to make the var type agree with the value?
|
|
render_const_value(codegen, name, var_scope->var->const_value);
|
|
return true;
|
|
}
|
|
|
|
static Buf *get_anon_type_name(CodeGen *codegen, IrExecutable *exec, const char *kind_name,
|
|
Scope *scope, AstNode *source_node, Buf *out_bare_name)
|
|
{
|
|
if (exec->name) {
|
|
ZigType *import = get_scope_import(scope);
|
|
Buf *namespace_name = buf_create_from_buf(&import->name);
|
|
if (buf_len(namespace_name) != 0) buf_append_char(namespace_name, NAMESPACE_SEP_CHAR);
|
|
buf_append_buf(namespace_name, exec->name);
|
|
buf_init_from_buf(out_bare_name, exec->name);
|
|
return namespace_name;
|
|
} else if (exec->name_fn != nullptr) {
|
|
Buf *name = buf_alloc();
|
|
buf_append_buf(name, &exec->name_fn->symbol_name);
|
|
buf_appendf(name, "(");
|
|
render_instance_name_recursive(codegen, name, &exec->name_fn->fndef_scope->base, exec->begin_scope);
|
|
buf_appendf(name, ")");
|
|
buf_init_from_buf(out_bare_name, name);
|
|
return name;
|
|
} else {
|
|
ZigType *import = get_scope_import(scope);
|
|
Buf *namespace_name = buf_create_from_buf(&import->name);
|
|
if (buf_len(namespace_name) != 0) buf_append_char(namespace_name, NAMESPACE_SEP_CHAR);
|
|
buf_appendf(namespace_name, "%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize, kind_name,
|
|
source_node->line + 1, source_node->column + 1);
|
|
buf_init_from_buf(out_bare_name, namespace_name);
|
|
return namespace_name;
|
|
}
|
|
}
|
|
|
|
static IrInstruction *ir_gen_container_decl(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
|
|
assert(node->type == NodeTypeContainerDecl);
|
|
|
|
ContainerKind kind = node->data.container_decl.kind;
|
|
Buf *bare_name = buf_alloc();
|
|
Buf *name = get_anon_type_name(irb->codegen, irb->exec, container_string(kind), parent_scope, node, bare_name);
|
|
|
|
ContainerLayout layout = node->data.container_decl.layout;
|
|
ZigType *container_type = get_partial_container_type(irb->codegen, parent_scope,
|
|
kind, node, buf_ptr(name), bare_name, layout);
|
|
ScopeDecls *child_scope = get_container_scope(container_type);
|
|
|
|
for (size_t i = 0; i < node->data.container_decl.decls.length; i += 1) {
|
|
AstNode *child_node = node->data.container_decl.decls.at(i);
|
|
scan_decls(irb->codegen, child_scope, child_node);
|
|
}
|
|
|
|
TldContainer *tld_container = allocate<TldContainer>(1);
|
|
init_tld(&tld_container->base, TldIdContainer, bare_name, VisibModPub, node, parent_scope);
|
|
tld_container->type_entry = container_type;
|
|
tld_container->decls_scope = child_scope;
|
|
irb->codegen->resolve_queue.append(&tld_container->base);
|
|
|
|
// Add this to the list to mark as invalid if analyzing this exec fails.
|
|
irb->exec->tld_list.append(&tld_container->base);
|
|
|
|
return ir_build_const_type(irb, parent_scope, node, container_type);
|
|
}
|
|
|
|
// errors should be populated with set1's values
|
|
static ZigType *get_error_set_union(CodeGen *g, ErrorTableEntry **errors, ZigType *set1, ZigType *set2) {
|
|
assert(set1->id == ZigTypeIdErrorSet);
|
|
assert(set2->id == ZigTypeIdErrorSet);
|
|
|
|
ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet);
|
|
err_set_type->size_in_bits = g->builtin_types.entry_global_error_set->size_in_bits;
|
|
err_set_type->abi_align = g->builtin_types.entry_global_error_set->abi_align;
|
|
err_set_type->abi_size = g->builtin_types.entry_global_error_set->abi_size;
|
|
buf_resize(&err_set_type->name, 0);
|
|
buf_appendf(&err_set_type->name, "error{");
|
|
|
|
for (uint32_t i = 0, count = set1->data.error_set.err_count; i < count; i += 1) {
|
|
assert(errors[set1->data.error_set.errors[i]->value] == set1->data.error_set.errors[i]);
|
|
}
|
|
|
|
uint32_t count = set1->data.error_set.err_count;
|
|
for (uint32_t i = 0; i < set2->data.error_set.err_count; i += 1) {
|
|
ErrorTableEntry *error_entry = set2->data.error_set.errors[i];
|
|
if (errors[error_entry->value] == nullptr) {
|
|
count += 1;
|
|
}
|
|
}
|
|
|
|
err_set_type->data.error_set.err_count = count;
|
|
err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(count);
|
|
|
|
for (uint32_t i = 0; i < set1->data.error_set.err_count; i += 1) {
|
|
ErrorTableEntry *error_entry = set1->data.error_set.errors[i];
|
|
buf_appendf(&err_set_type->name, "%s,", buf_ptr(&error_entry->name));
|
|
err_set_type->data.error_set.errors[i] = error_entry;
|
|
}
|
|
|
|
uint32_t index = set1->data.error_set.err_count;
|
|
for (uint32_t i = 0; i < set2->data.error_set.err_count; i += 1) {
|
|
ErrorTableEntry *error_entry = set2->data.error_set.errors[i];
|
|
if (errors[error_entry->value] == nullptr) {
|
|
errors[error_entry->value] = error_entry;
|
|
buf_appendf(&err_set_type->name, "%s,", buf_ptr(&error_entry->name));
|
|
err_set_type->data.error_set.errors[index] = error_entry;
|
|
index += 1;
|
|
}
|
|
}
|
|
assert(index == count);
|
|
assert(count != 0);
|
|
|
|
buf_appendf(&err_set_type->name, "}");
|
|
|
|
return err_set_type;
|
|
|
|
}
|
|
|
|
static ZigType *make_err_set_with_one_item(CodeGen *g, Scope *parent_scope, AstNode *node,
|
|
ErrorTableEntry *err_entry)
|
|
{
|
|
ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet);
|
|
buf_resize(&err_set_type->name, 0);
|
|
buf_appendf(&err_set_type->name, "error{%s}", buf_ptr(&err_entry->name));
|
|
err_set_type->size_in_bits = g->builtin_types.entry_global_error_set->size_in_bits;
|
|
err_set_type->abi_align = g->builtin_types.entry_global_error_set->abi_align;
|
|
err_set_type->abi_size = g->builtin_types.entry_global_error_set->abi_size;
|
|
err_set_type->data.error_set.err_count = 1;
|
|
err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(1);
|
|
|
|
err_set_type->data.error_set.errors[0] = err_entry;
|
|
|
|
return err_set_type;
|
|
}
|
|
|
|
static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
|
|
assert(node->type == NodeTypeErrorSetDecl);
|
|
|
|
uint32_t err_count = node->data.err_set_decl.decls.length;
|
|
|
|
Buf bare_name = BUF_INIT;
|
|
Buf *type_name = get_anon_type_name(irb->codegen, irb->exec, "error", parent_scope, node, &bare_name);
|
|
ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet);
|
|
buf_init_from_buf(&err_set_type->name, type_name);
|
|
err_set_type->data.error_set.err_count = err_count;
|
|
err_set_type->size_in_bits = irb->codegen->builtin_types.entry_global_error_set->size_in_bits;
|
|
err_set_type->abi_align = irb->codegen->builtin_types.entry_global_error_set->abi_align;
|
|
err_set_type->abi_size = irb->codegen->builtin_types.entry_global_error_set->abi_size;
|
|
err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(err_count);
|
|
|
|
ErrorTableEntry **errors = allocate<ErrorTableEntry *>(irb->codegen->errors_by_index.length + err_count);
|
|
|
|
for (uint32_t i = 0; i < err_count; i += 1) {
|
|
AstNode *symbol_node = node->data.err_set_decl.decls.at(i);
|
|
assert(symbol_node->type == NodeTypeSymbol);
|
|
Buf *err_name = symbol_node->data.symbol_expr.symbol;
|
|
ErrorTableEntry *err = allocate<ErrorTableEntry>(1);
|
|
err->decl_node = symbol_node;
|
|
buf_init_from_buf(&err->name, err_name);
|
|
|
|
auto existing_entry = irb->codegen->error_table.put_unique(err_name, err);
|
|
if (existing_entry) {
|
|
err->value = existing_entry->value->value;
|
|
} else {
|
|
size_t error_value_count = irb->codegen->errors_by_index.length;
|
|
assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)irb->codegen->err_tag_type->data.integral.bit_count));
|
|
err->value = error_value_count;
|
|
irb->codegen->errors_by_index.append(err);
|
|
}
|
|
err_set_type->data.error_set.errors[i] = err;
|
|
|
|
ErrorTableEntry *prev_err = errors[err->value];
|
|
if (prev_err != nullptr) {
|
|
ErrorMsg *msg = add_node_error(irb->codegen, err->decl_node, buf_sprintf("duplicate error: '%s'", buf_ptr(&err->name)));
|
|
add_error_note(irb->codegen, msg, prev_err->decl_node, buf_sprintf("other error here"));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
errors[err->value] = err;
|
|
}
|
|
free(errors);
|
|
return ir_build_const_type(irb, parent_scope, node, err_set_type);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
|
|
assert(node->type == NodeTypeFnProto);
|
|
|
|
size_t param_count = node->data.fn_proto.params.length;
|
|
IrInstruction **param_types = allocate<IrInstruction*>(param_count);
|
|
|
|
bool is_var_args = false;
|
|
for (size_t i = 0; i < param_count; i += 1) {
|
|
AstNode *param_node = node->data.fn_proto.params.at(i);
|
|
if (param_node->data.param_decl.is_var_args) {
|
|
is_var_args = true;
|
|
break;
|
|
}
|
|
if (param_node->data.param_decl.var_token == nullptr) {
|
|
AstNode *type_node = param_node->data.param_decl.type;
|
|
IrInstruction *type_value = ir_gen_node(irb, type_node, parent_scope);
|
|
if (type_value == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
param_types[i] = type_value;
|
|
} else {
|
|
param_types[i] = nullptr;
|
|
}
|
|
}
|
|
|
|
IrInstruction *align_value = nullptr;
|
|
if (node->data.fn_proto.align_expr != nullptr) {
|
|
align_value = ir_gen_node(irb, node->data.fn_proto.align_expr, parent_scope);
|
|
if (align_value == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *return_type;
|
|
if (node->data.fn_proto.return_var_token == nullptr) {
|
|
if (node->data.fn_proto.return_type == nullptr) {
|
|
return_type = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_void);
|
|
} else {
|
|
return_type = ir_gen_node(irb, node->data.fn_proto.return_type, parent_scope);
|
|
if (return_type == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
} else {
|
|
add_node_error(irb->codegen, node,
|
|
buf_sprintf("TODO implement inferred return types https://github.com/ziglang/zig/issues/447"));
|
|
return irb->codegen->invalid_instruction;
|
|
//return_type = nullptr;
|
|
}
|
|
|
|
return ir_build_fn_proto(irb, parent_scope, node, param_types, align_value, return_type, is_var_args);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *scope, AstNode *node) {
|
|
assert(node->type == NodeTypeCancel);
|
|
|
|
IrInstruction *target_inst = ir_gen_node(irb, node->data.cancel_expr.expr, scope);
|
|
if (target_inst == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
zig_panic("TODO ir_gen_cancel");
|
|
}
|
|
|
|
static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) {
|
|
assert(node->type == NodeTypeResume);
|
|
|
|
IrInstruction *target_inst = ir_gen_node_extra(irb, node->data.resume_expr.expr, scope, LValPtr, nullptr);
|
|
if (target_inst == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
return ir_build_coro_resume(irb, scope, node, target_inst);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval,
|
|
ResultLoc *result_loc)
|
|
{
|
|
assert(node->type == NodeTypeAwaitExpr);
|
|
|
|
ZigFn *fn_entry = exec_fn_entry(irb->exec);
|
|
if (!fn_entry) {
|
|
add_node_error(irb->codegen, node, buf_sprintf("await outside function definition"));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
ScopeSuspend *existing_suspend_scope = get_scope_suspend(scope);
|
|
if (existing_suspend_scope) {
|
|
if (!existing_suspend_scope->reported_err) {
|
|
ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot await inside suspend block"));
|
|
add_error_note(irb->codegen, msg, existing_suspend_scope->base.source_node, buf_sprintf("suspend block here"));
|
|
existing_suspend_scope->reported_err = true;
|
|
}
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *target_inst = ir_gen_node_extra(irb, node->data.await_expr.expr, scope, LValPtr, nullptr);
|
|
if (target_inst == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
IrInstruction *await_inst = ir_build_await_src(irb, scope, node, target_inst, result_loc);
|
|
return ir_lval_wrap(irb, scope, await_inst, lval, result_loc);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
|
|
assert(node->type == NodeTypeSuspend);
|
|
|
|
ZigFn *fn_entry = exec_fn_entry(irb->exec);
|
|
if (!fn_entry) {
|
|
add_node_error(irb->codegen, node, buf_sprintf("suspend outside function definition"));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
ScopeSuspend *existing_suspend_scope = get_scope_suspend(parent_scope);
|
|
if (existing_suspend_scope) {
|
|
if (!existing_suspend_scope->reported_err) {
|
|
ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot suspend inside suspend block"));
|
|
add_error_note(irb->codegen, msg, existing_suspend_scope->base.source_node, buf_sprintf("other suspend block here"));
|
|
existing_suspend_scope->reported_err = true;
|
|
}
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstructionSuspendBegin *begin = ir_build_suspend_begin(irb, parent_scope, node);
|
|
if (node->data.suspend.block != nullptr) {
|
|
ScopeSuspend *suspend_scope = create_suspend_scope(irb->codegen, node, parent_scope);
|
|
Scope *child_scope = &suspend_scope->base;
|
|
IrInstruction *susp_res = ir_gen_node(irb, node->data.suspend.block, child_scope);
|
|
ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.suspend.block, susp_res));
|
|
}
|
|
|
|
return ir_build_suspend_finish(irb, parent_scope, node, begin);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope,
|
|
LVal lval, ResultLoc *result_loc)
|
|
{
|
|
assert(scope);
|
|
switch (node->type) {
|
|
case NodeTypeStructValueField:
|
|
case NodeTypeParamDecl:
|
|
case NodeTypeUsingNamespace:
|
|
case NodeTypeSwitchProng:
|
|
case NodeTypeSwitchRange:
|
|
case NodeTypeStructField:
|
|
case NodeTypeFnDef:
|
|
case NodeTypeTestDecl:
|
|
zig_unreachable();
|
|
case NodeTypeBlock:
|
|
return ir_gen_block(irb, scope, node, lval, result_loc);
|
|
case NodeTypeGroupedExpr:
|
|
return ir_gen_node_raw(irb, node->data.grouped_expr, scope, lval, result_loc);
|
|
case NodeTypeBinOpExpr:
|
|
return ir_gen_bin_op(irb, scope, node, lval, result_loc);
|
|
case NodeTypeIntLiteral:
|
|
return ir_lval_wrap(irb, scope, ir_gen_int_lit(irb, scope, node), lval, result_loc);
|
|
case NodeTypeFloatLiteral:
|
|
return ir_lval_wrap(irb, scope, ir_gen_float_lit(irb, scope, node), lval, result_loc);
|
|
case NodeTypeCharLiteral:
|
|
return ir_lval_wrap(irb, scope, ir_gen_char_lit(irb, scope, node), lval, result_loc);
|
|
case NodeTypeSymbol:
|
|
return ir_gen_symbol(irb, scope, node, lval, result_loc);
|
|
case NodeTypeFnCallExpr:
|
|
return ir_gen_fn_call(irb, scope, node, lval, result_loc);
|
|
case NodeTypeIfBoolExpr:
|
|
return ir_gen_if_bool_expr(irb, scope, node, lval, result_loc);
|
|
case NodeTypePrefixOpExpr:
|
|
return ir_gen_prefix_op_expr(irb, scope, node, lval, result_loc);
|
|
case NodeTypeContainerInitExpr:
|
|
return ir_gen_container_init_expr(irb, scope, node, lval, result_loc);
|
|
case NodeTypeVariableDeclaration:
|
|
return ir_gen_var_decl(irb, scope, node);
|
|
case NodeTypeWhileExpr:
|
|
return ir_gen_while_expr(irb, scope, node, lval, result_loc);
|
|
case NodeTypeForExpr:
|
|
return ir_gen_for_expr(irb, scope, node, lval, result_loc);
|
|
case NodeTypeArrayAccessExpr:
|
|
return ir_gen_array_access(irb, scope, node, lval, result_loc);
|
|
case NodeTypeReturnExpr:
|
|
return ir_gen_return(irb, scope, node, lval, result_loc);
|
|
case NodeTypeFieldAccessExpr:
|
|
{
|
|
IrInstruction *ptr_instruction = ir_gen_field_access(irb, scope, node);
|
|
if (ptr_instruction == irb->codegen->invalid_instruction)
|
|
return ptr_instruction;
|
|
if (lval == LValPtr)
|
|
return ptr_instruction;
|
|
|
|
IrInstruction *load_ptr = ir_build_load_ptr(irb, scope, node, ptr_instruction);
|
|
return ir_expr_wrap(irb, scope, load_ptr, result_loc);
|
|
}
|
|
case NodeTypePtrDeref: {
|
|
AstNode *expr_node = node->data.ptr_deref_expr.target;
|
|
IrInstruction *value = ir_gen_node_extra(irb, expr_node, scope, lval, nullptr);
|
|
if (value == irb->codegen->invalid_instruction)
|
|
return value;
|
|
|
|
// We essentially just converted any lvalue from &(x.*) to (&x).*;
|
|
// this inhibits checking that x is a pointer later, so we directly
|
|
// record whether the pointer check is needed
|
|
IrInstruction *un_op = ir_build_un_op_lval(irb, scope, node, IrUnOpDereference, value, lval, result_loc);
|
|
return ir_expr_wrap(irb, scope, un_op, result_loc);
|
|
}
|
|
case NodeTypeUnwrapOptional: {
|
|
AstNode *expr_node = node->data.unwrap_optional.expr;
|
|
|
|
IrInstruction *maybe_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr);
|
|
if (maybe_ptr == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
IrInstruction *unwrapped_ptr = ir_build_optional_unwrap_ptr(irb, scope, node, maybe_ptr, true, false);
|
|
if (lval == LValPtr)
|
|
return unwrapped_ptr;
|
|
|
|
IrInstruction *load_ptr = ir_build_load_ptr(irb, scope, node, unwrapped_ptr);
|
|
return ir_expr_wrap(irb, scope, load_ptr, result_loc);
|
|
}
|
|
case NodeTypeBoolLiteral:
|
|
return ir_lval_wrap(irb, scope, ir_gen_bool_literal(irb, scope, node), lval, result_loc);
|
|
case NodeTypeArrayType:
|
|
return ir_lval_wrap(irb, scope, ir_gen_array_type(irb, scope, node), lval, result_loc);
|
|
case NodeTypePointerType:
|
|
return ir_lval_wrap(irb, scope, ir_gen_pointer_type(irb, scope, node), lval, result_loc);
|
|
case NodeTypeAnyFrameType:
|
|
return ir_lval_wrap(irb, scope, ir_gen_anyframe_type(irb, scope, node), lval, result_loc);
|
|
case NodeTypeStringLiteral:
|
|
return ir_lval_wrap(irb, scope, ir_gen_string_literal(irb, scope, node), lval, result_loc);
|
|
case NodeTypeUndefinedLiteral:
|
|
return ir_lval_wrap(irb, scope, ir_gen_undefined_literal(irb, scope, node), lval, result_loc);
|
|
case NodeTypeAsmExpr:
|
|
return ir_lval_wrap(irb, scope, ir_gen_asm_expr(irb, scope, node), lval, result_loc);
|
|
case NodeTypeNullLiteral:
|
|
return ir_lval_wrap(irb, scope, ir_gen_null_literal(irb, scope, node), lval, result_loc);
|
|
case NodeTypeIfErrorExpr:
|
|
return ir_gen_if_err_expr(irb, scope, node, lval, result_loc);
|
|
case NodeTypeIfOptional:
|
|
return ir_gen_if_optional_expr(irb, scope, node, lval, result_loc);
|
|
case NodeTypeSwitchExpr:
|
|
return ir_gen_switch_expr(irb, scope, node, lval, result_loc);
|
|
case NodeTypeCompTime:
|
|
return ir_expr_wrap(irb, scope, ir_gen_comptime(irb, scope, node, lval), result_loc);
|
|
case NodeTypeErrorType:
|
|
return ir_lval_wrap(irb, scope, ir_gen_error_type(irb, scope, node), lval, result_loc);
|
|
case NodeTypeBreak:
|
|
return ir_lval_wrap(irb, scope, ir_gen_break(irb, scope, node), lval, result_loc);
|
|
case NodeTypeContinue:
|
|
return ir_lval_wrap(irb, scope, ir_gen_continue(irb, scope, node), lval, result_loc);
|
|
case NodeTypeUnreachable:
|
|
return ir_build_unreachable(irb, scope, node);
|
|
case NodeTypeDefer:
|
|
return ir_lval_wrap(irb, scope, ir_gen_defer(irb, scope, node), lval, result_loc);
|
|
case NodeTypeSliceExpr:
|
|
return ir_gen_slice(irb, scope, node, lval, result_loc);
|
|
case NodeTypeCatchExpr:
|
|
return ir_gen_catch(irb, scope, node, lval, result_loc);
|
|
case NodeTypeContainerDecl:
|
|
return ir_lval_wrap(irb, scope, ir_gen_container_decl(irb, scope, node), lval, result_loc);
|
|
case NodeTypeFnProto:
|
|
return ir_lval_wrap(irb, scope, ir_gen_fn_proto(irb, scope, node), lval, result_loc);
|
|
case NodeTypeErrorSetDecl:
|
|
return ir_lval_wrap(irb, scope, ir_gen_err_set_decl(irb, scope, node), lval, result_loc);
|
|
case NodeTypeCancel:
|
|
return ir_lval_wrap(irb, scope, ir_gen_cancel(irb, scope, node), lval, result_loc);
|
|
case NodeTypeResume:
|
|
return ir_lval_wrap(irb, scope, ir_gen_resume(irb, scope, node), lval, result_loc);
|
|
case NodeTypeAwaitExpr:
|
|
return ir_gen_await_expr(irb, scope, node, lval, result_loc);
|
|
case NodeTypeSuspend:
|
|
return ir_lval_wrap(irb, scope, ir_gen_suspend(irb, scope, node), lval, result_loc);
|
|
case NodeTypeEnumLiteral:
|
|
return ir_lval_wrap(irb, scope, ir_gen_enum_literal(irb, scope, node), lval, result_loc);
|
|
case NodeTypeInferredArrayType:
|
|
add_node_error(irb->codegen, node,
|
|
buf_sprintf("inferred array size invalid here"));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
static ResultLoc *no_result_loc(void) {
|
|
ResultLocNone *result_loc_none = allocate<ResultLocNone>(1);
|
|
result_loc_none->base.id = ResultLocIdNone;
|
|
return &result_loc_none->base;
|
|
}
|
|
|
|
static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval,
|
|
ResultLoc *result_loc)
|
|
{
|
|
if (result_loc == nullptr) {
|
|
// Create a result location indicating there is none - but if one gets created
|
|
// it will be properly distributed.
|
|
result_loc = no_result_loc();
|
|
ir_build_reset_result(irb, scope, node, result_loc);
|
|
}
|
|
IrInstruction *result = ir_gen_node_raw(irb, node, scope, lval, result_loc);
|
|
irb->exec->invalid = irb->exec->invalid || (result == irb->codegen->invalid_instruction);
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope) {
|
|
return ir_gen_node_extra(irb, node, scope, LValNone, nullptr);
|
|
}
|
|
|
|
static void invalidate_exec(IrExecutable *exec) {
|
|
if (exec->invalid)
|
|
return;
|
|
|
|
exec->invalid = true;
|
|
|
|
for (size_t i = 0; i < exec->tld_list.length; i += 1) {
|
|
exec->tld_list.items[i]->resolution = TldResolutionInvalid;
|
|
}
|
|
|
|
if (exec->source_exec != nullptr)
|
|
invalidate_exec(exec->source_exec);
|
|
}
|
|
|
|
|
|
bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_executable) {
|
|
assert(node->owner);
|
|
|
|
IrBuilder ir_builder = {0};
|
|
IrBuilder *irb = &ir_builder;
|
|
|
|
irb->codegen = codegen;
|
|
irb->exec = ir_executable;
|
|
|
|
IrBasicBlock *entry_block = ir_create_basic_block(irb, scope, "Entry");
|
|
ir_set_cursor_at_end_and_append_block(irb, entry_block);
|
|
// Entry block gets a reference because we enter it to begin.
|
|
ir_ref_bb(irb->current_basic_block);
|
|
|
|
IrInstruction *result = ir_gen_node_extra(irb, node, scope, LValNone, nullptr);
|
|
assert(result);
|
|
if (irb->exec->invalid)
|
|
return false;
|
|
|
|
if (!instr_is_unreachable(result)) {
|
|
ir_mark_gen(ir_build_return_begin(irb, scope, node, result));
|
|
// no need for save_err_ret_addr because this cannot return error
|
|
ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, result->source_node, result));
|
|
ir_mark_gen(ir_build_return(irb, scope, result->source_node, result));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ir_gen_fn(CodeGen *codegen, ZigFn *fn_entry) {
|
|
assert(fn_entry);
|
|
|
|
IrExecutable *ir_executable = &fn_entry->ir_executable;
|
|
AstNode *body_node = fn_entry->body_node;
|
|
|
|
assert(fn_entry->child_scope);
|
|
|
|
return ir_gen(codegen, body_node, fn_entry->child_scope, ir_executable);
|
|
}
|
|
|
|
static void add_call_stack_errors(CodeGen *codegen, IrExecutable *exec, ErrorMsg *err_msg, int limit) {
|
|
if (!exec || !exec->source_node || limit < 0) return;
|
|
add_error_note(codegen, err_msg, exec->source_node, buf_sprintf("called from here"));
|
|
|
|
add_call_stack_errors(codegen, exec->parent_exec, err_msg, limit - 1);
|
|
}
|
|
|
|
static ErrorMsg *exec_add_error_node(CodeGen *codegen, IrExecutable *exec, AstNode *source_node, Buf *msg) {
|
|
invalidate_exec(exec);
|
|
ErrorMsg *err_msg = add_node_error(codegen, source_node, msg);
|
|
if (exec->parent_exec) {
|
|
add_call_stack_errors(codegen, exec, err_msg, 10);
|
|
}
|
|
return err_msg;
|
|
}
|
|
|
|
static ErrorMsg *ir_add_error_node(IrAnalyze *ira, AstNode *source_node, Buf *msg) {
|
|
return exec_add_error_node(ira->codegen, ira->new_irb.exec, source_node, msg);
|
|
}
|
|
|
|
static ErrorMsg *opt_ir_add_error_node(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, Buf *msg) {
|
|
if (ira != nullptr)
|
|
return exec_add_error_node(codegen, ira->new_irb.exec, source_node, msg);
|
|
else
|
|
return add_node_error(codegen, source_node, msg);
|
|
}
|
|
|
|
static ErrorMsg *ir_add_error(IrAnalyze *ira, IrInstruction *source_instruction, Buf *msg) {
|
|
return ir_add_error_node(ira, source_instruction->source_node, msg);
|
|
}
|
|
|
|
static void ir_assert(bool ok, IrInstruction *source_instruction) {
|
|
if (ok) return;
|
|
src_assert(ok, source_instruction->source_node);
|
|
}
|
|
|
|
// This function takes a comptime ptr and makes the child const value conform to the type
|
|
// described by the pointer.
|
|
static Error eval_comptime_ptr_reinterpret(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node,
|
|
ConstExprValue *ptr_val)
|
|
{
|
|
Error err;
|
|
assert(ptr_val->type->id == ZigTypeIdPointer);
|
|
ConstExprValue tmp = {};
|
|
tmp.special = ConstValSpecialStatic;
|
|
tmp.type = ptr_val->type->data.pointer.child_type;
|
|
if ((err = ir_read_const_ptr(ira, codegen, source_node, &tmp, ptr_val)))
|
|
return err;
|
|
ConstExprValue *child_val = const_ptr_pointee_unchecked(codegen, ptr_val);
|
|
copy_const_val(child_val, &tmp, false);
|
|
return ErrorNone;
|
|
}
|
|
|
|
ConstExprValue *const_ptr_pointee(IrAnalyze *ira, CodeGen *codegen, ConstExprValue *const_val,
|
|
AstNode *source_node)
|
|
{
|
|
Error err;
|
|
ConstExprValue *val = const_ptr_pointee_unchecked(codegen, const_val);
|
|
assert(val != nullptr);
|
|
assert(const_val->type->id == ZigTypeIdPointer);
|
|
ZigType *expected_type = const_val->type->data.pointer.child_type;
|
|
if (!types_have_same_zig_comptime_repr(val->type, expected_type)) {
|
|
if ((err = eval_comptime_ptr_reinterpret(ira, codegen, source_node, const_val)))
|
|
return nullptr;
|
|
return const_ptr_pointee_unchecked(codegen, const_val);
|
|
}
|
|
return val;
|
|
}
|
|
|
|
static ConstExprValue *ir_exec_const_result(CodeGen *codegen, IrExecutable *exec) {
|
|
IrBasicBlock *bb = exec->basic_block_list.at(0);
|
|
for (size_t i = 0; i < bb->instruction_list.length; i += 1) {
|
|
IrInstruction *instruction = bb->instruction_list.at(i);
|
|
if (instruction->id == IrInstructionIdReturn) {
|
|
IrInstructionReturn *ret_inst = (IrInstructionReturn *)instruction;
|
|
IrInstruction *operand = ret_inst->operand;
|
|
if (operand->value.special == ConstValSpecialRuntime) {
|
|
exec_add_error_node(codegen, exec, operand->source_node,
|
|
buf_sprintf("unable to evaluate constant expression"));
|
|
return &codegen->invalid_instruction->value;
|
|
}
|
|
return &operand->value;
|
|
} else if (ir_has_side_effects(instruction)) {
|
|
if (instr_is_comptime(instruction)) {
|
|
switch (instruction->id) {
|
|
case IrInstructionIdUnwrapErrPayload:
|
|
case IrInstructionIdUnionFieldPtr:
|
|
case IrInstructionIdReturnBegin:
|
|
continue;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
exec_add_error_node(codegen, exec, instruction->source_node,
|
|
buf_sprintf("unable to evaluate constant expression"));
|
|
return &codegen->invalid_instruction->value;
|
|
}
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
static bool ir_emit_global_runtime_side_effect(IrAnalyze *ira, IrInstruction *source_instruction) {
|
|
if (ir_should_inline(ira->new_irb.exec, source_instruction->scope)) {
|
|
ir_add_error(ira, source_instruction, buf_sprintf("unable to evaluate constant expression"));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool const_val_fits_in_num_lit(ConstExprValue *const_val, ZigType *num_lit_type) {
|
|
return ((num_lit_type->id == ZigTypeIdComptimeFloat &&
|
|
(const_val->type->id == ZigTypeIdFloat || const_val->type->id == ZigTypeIdComptimeFloat)) ||
|
|
(num_lit_type->id == ZigTypeIdComptimeInt &&
|
|
(const_val->type->id == ZigTypeIdInt || const_val->type->id == ZigTypeIdComptimeInt)));
|
|
}
|
|
|
|
static bool float_has_fraction(ConstExprValue *const_val) {
|
|
if (const_val->type->id == ZigTypeIdComptimeFloat) {
|
|
return bigfloat_has_fraction(&const_val->data.x_bigfloat);
|
|
} else if (const_val->type->id == ZigTypeIdFloat) {
|
|
switch (const_val->type->data.floating.bit_count) {
|
|
case 16:
|
|
{
|
|
float16_t floored = f16_roundToInt(const_val->data.x_f16, softfloat_round_minMag, false);
|
|
return !f16_eq(floored, const_val->data.x_f16);
|
|
}
|
|
case 32:
|
|
return floorf(const_val->data.x_f32) != const_val->data.x_f32;
|
|
case 64:
|
|
return floor(const_val->data.x_f64) != const_val->data.x_f64;
|
|
case 128:
|
|
{
|
|
float128_t floored;
|
|
f128M_roundToInt(&const_val->data.x_f128, softfloat_round_minMag, false, &floored);
|
|
return !f128M_eq(&floored, &const_val->data.x_f128);
|
|
}
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
|
|
static void float_append_buf(Buf *buf, ConstExprValue *const_val) {
|
|
if (const_val->type->id == ZigTypeIdComptimeFloat) {
|
|
bigfloat_append_buf(buf, &const_val->data.x_bigfloat);
|
|
} else if (const_val->type->id == ZigTypeIdFloat) {
|
|
switch (const_val->type->data.floating.bit_count) {
|
|
case 16:
|
|
buf_appendf(buf, "%f", zig_f16_to_double(const_val->data.x_f16));
|
|
break;
|
|
case 32:
|
|
buf_appendf(buf, "%f", const_val->data.x_f32);
|
|
break;
|
|
case 64:
|
|
buf_appendf(buf, "%f", const_val->data.x_f64);
|
|
break;
|
|
case 128:
|
|
{
|
|
// TODO actual implementation
|
|
const size_t extra_len = 100;
|
|
size_t old_len = buf_len(buf);
|
|
buf_resize(buf, old_len + extra_len);
|
|
|
|
float64_t f64_value = f128M_to_f64(&const_val->data.x_f128);
|
|
double double_value;
|
|
memcpy(&double_value, &f64_value, sizeof(double));
|
|
|
|
int len = snprintf(buf_ptr(buf) + old_len, extra_len, "%f", double_value);
|
|
assert(len > 0);
|
|
buf_resize(buf, old_len + len);
|
|
break;
|
|
}
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
|
|
static void float_init_bigint(BigInt *bigint, ConstExprValue *const_val) {
|
|
if (const_val->type->id == ZigTypeIdComptimeFloat) {
|
|
bigint_init_bigfloat(bigint, &const_val->data.x_bigfloat);
|
|
} else if (const_val->type->id == ZigTypeIdFloat) {
|
|
switch (const_val->type->data.floating.bit_count) {
|
|
case 16:
|
|
{
|
|
double x = zig_f16_to_double(const_val->data.x_f16);
|
|
if (x >= 0) {
|
|
bigint_init_unsigned(bigint, (uint64_t)x);
|
|
} else {
|
|
bigint_init_unsigned(bigint, (uint64_t)-x);
|
|
bigint->is_negative = true;
|
|
}
|
|
break;
|
|
}
|
|
case 32:
|
|
if (const_val->data.x_f32 >= 0) {
|
|
bigint_init_unsigned(bigint, (uint64_t)(const_val->data.x_f32));
|
|
} else {
|
|
bigint_init_unsigned(bigint, (uint64_t)(-const_val->data.x_f32));
|
|
bigint->is_negative = true;
|
|
}
|
|
break;
|
|
case 64:
|
|
if (const_val->data.x_f64 >= 0) {
|
|
bigint_init_unsigned(bigint, (uint64_t)(const_val->data.x_f64));
|
|
} else {
|
|
bigint_init_unsigned(bigint, (uint64_t)(-const_val->data.x_f64));
|
|
bigint->is_negative = true;
|
|
}
|
|
break;
|
|
case 128:
|
|
{
|
|
BigFloat tmp_float;
|
|
bigfloat_init_128(&tmp_float, const_val->data.x_f128);
|
|
bigint_init_bigfloat(bigint, &tmp_float);
|
|
}
|
|
break;
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
|
|
static void float_init_bigfloat(ConstExprValue *dest_val, BigFloat *bigfloat) {
|
|
if (dest_val->type->id == ZigTypeIdComptimeFloat) {
|
|
bigfloat_init_bigfloat(&dest_val->data.x_bigfloat, bigfloat);
|
|
} else if (dest_val->type->id == ZigTypeIdFloat) {
|
|
switch (dest_val->type->data.floating.bit_count) {
|
|
case 16:
|
|
dest_val->data.x_f16 = bigfloat_to_f16(bigfloat);
|
|
break;
|
|
case 32:
|
|
dest_val->data.x_f32 = bigfloat_to_f32(bigfloat);
|
|
break;
|
|
case 64:
|
|
dest_val->data.x_f64 = bigfloat_to_f64(bigfloat);
|
|
break;
|
|
case 80:
|
|
zig_panic("TODO");
|
|
case 128:
|
|
dest_val->data.x_f128 = bigfloat_to_f128(bigfloat);
|
|
break;
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
|
|
static void float_init_f16(ConstExprValue *dest_val, float16_t x) {
|
|
if (dest_val->type->id == ZigTypeIdComptimeFloat) {
|
|
bigfloat_init_16(&dest_val->data.x_bigfloat, x);
|
|
} else if (dest_val->type->id == ZigTypeIdFloat) {
|
|
switch (dest_val->type->data.floating.bit_count) {
|
|
case 16:
|
|
dest_val->data.x_f16 = x;
|
|
break;
|
|
case 32:
|
|
dest_val->data.x_f32 = zig_f16_to_double(x);
|
|
break;
|
|
case 64:
|
|
dest_val->data.x_f64 = zig_f16_to_double(x);
|
|
break;
|
|
case 128:
|
|
f16_to_f128M(x, &dest_val->data.x_f128);
|
|
break;
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
|
|
static void float_init_f32(ConstExprValue *dest_val, float x) {
|
|
if (dest_val->type->id == ZigTypeIdComptimeFloat) {
|
|
bigfloat_init_32(&dest_val->data.x_bigfloat, x);
|
|
} else if (dest_val->type->id == ZigTypeIdFloat) {
|
|
switch (dest_val->type->data.floating.bit_count) {
|
|
case 16:
|
|
dest_val->data.x_f16 = zig_double_to_f16(x);
|
|
break;
|
|
case 32:
|
|
dest_val->data.x_f32 = x;
|
|
break;
|
|
case 64:
|
|
dest_val->data.x_f64 = x;
|
|
break;
|
|
case 128:
|
|
{
|
|
float32_t x_f32;
|
|
memcpy(&x_f32, &x, sizeof(float));
|
|
f32_to_f128M(x_f32, &dest_val->data.x_f128);
|
|
break;
|
|
}
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
|
|
static void float_init_f64(ConstExprValue *dest_val, double x) {
|
|
if (dest_val->type->id == ZigTypeIdComptimeFloat) {
|
|
bigfloat_init_64(&dest_val->data.x_bigfloat, x);
|
|
} else if (dest_val->type->id == ZigTypeIdFloat) {
|
|
switch (dest_val->type->data.floating.bit_count) {
|
|
case 16:
|
|
dest_val->data.x_f16 = zig_double_to_f16(x);
|
|
break;
|
|
case 32:
|
|
dest_val->data.x_f32 = x;
|
|
break;
|
|
case 64:
|
|
dest_val->data.x_f64 = x;
|
|
break;
|
|
case 128:
|
|
{
|
|
float64_t x_f64;
|
|
memcpy(&x_f64, &x, sizeof(double));
|
|
f64_to_f128M(x_f64, &dest_val->data.x_f128);
|
|
break;
|
|
}
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
|
|
static void float_init_f128(ConstExprValue *dest_val, float128_t x) {
|
|
if (dest_val->type->id == ZigTypeIdComptimeFloat) {
|
|
bigfloat_init_128(&dest_val->data.x_bigfloat, x);
|
|
} else if (dest_val->type->id == ZigTypeIdFloat) {
|
|
switch (dest_val->type->data.floating.bit_count) {
|
|
case 16:
|
|
dest_val->data.x_f16 = f128M_to_f16(&x);
|
|
break;
|
|
case 32:
|
|
{
|
|
float32_t f32_val = f128M_to_f32(&x);
|
|
memcpy(&dest_val->data.x_f32, &f32_val, sizeof(float));
|
|
break;
|
|
}
|
|
case 64:
|
|
{
|
|
float64_t f64_val = f128M_to_f64(&x);
|
|
memcpy(&dest_val->data.x_f64, &f64_val, sizeof(double));
|
|
break;
|
|
}
|
|
case 128:
|
|
{
|
|
memcpy(&dest_val->data.x_f128, &x, sizeof(float128_t));
|
|
break;
|
|
}
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
|
|
static void float_init_float(ConstExprValue *dest_val, ConstExprValue *src_val) {
|
|
if (src_val->type->id == ZigTypeIdComptimeFloat) {
|
|
float_init_bigfloat(dest_val, &src_val->data.x_bigfloat);
|
|
} else if (src_val->type->id == ZigTypeIdFloat) {
|
|
switch (src_val->type->data.floating.bit_count) {
|
|
case 16:
|
|
float_init_f16(dest_val, src_val->data.x_f16);
|
|
break;
|
|
case 32:
|
|
float_init_f32(dest_val, src_val->data.x_f32);
|
|
break;
|
|
case 64:
|
|
float_init_f64(dest_val, src_val->data.x_f64);
|
|
break;
|
|
case 128:
|
|
float_init_f128(dest_val, src_val->data.x_f128);
|
|
break;
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
|
|
static bool float_is_nan(ConstExprValue *op) {
|
|
if (op->type->id == ZigTypeIdComptimeFloat) {
|
|
return bigfloat_is_nan(&op->data.x_bigfloat);
|
|
} else if (op->type->id == ZigTypeIdFloat) {
|
|
switch (op->type->data.floating.bit_count) {
|
|
case 16:
|
|
return f16_isSignalingNaN(op->data.x_f16);
|
|
case 32:
|
|
return op->data.x_f32 != op->data.x_f32;
|
|
case 64:
|
|
return op->data.x_f64 != op->data.x_f64;
|
|
case 128:
|
|
return f128M_isSignalingNaN(&op->data.x_f128);
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
|
|
static Cmp float_cmp(ConstExprValue *op1, ConstExprValue *op2) {
|
|
assert(op1->type == op2->type);
|
|
if (op1->type->id == ZigTypeIdComptimeFloat) {
|
|
return bigfloat_cmp(&op1->data.x_bigfloat, &op2->data.x_bigfloat);
|
|
} else if (op1->type->id == ZigTypeIdFloat) {
|
|
switch (op1->type->data.floating.bit_count) {
|
|
case 16:
|
|
if (f16_lt(op1->data.x_f16, op2->data.x_f16)) {
|
|
return CmpLT;
|
|
} else if (f16_lt(op2->data.x_f16, op1->data.x_f16)) {
|
|
return CmpGT;
|
|
} else {
|
|
return CmpEQ;
|
|
}
|
|
case 32:
|
|
if (op1->data.x_f32 > op2->data.x_f32) {
|
|
return CmpGT;
|
|
} else if (op1->data.x_f32 < op2->data.x_f32) {
|
|
return CmpLT;
|
|
} else {
|
|
return CmpEQ;
|
|
}
|
|
case 64:
|
|
if (op1->data.x_f64 > op2->data.x_f64) {
|
|
return CmpGT;
|
|
} else if (op1->data.x_f64 < op2->data.x_f64) {
|
|
return CmpLT;
|
|
} else {
|
|
return CmpEQ;
|
|
}
|
|
case 128:
|
|
if (f128M_lt(&op1->data.x_f128, &op2->data.x_f128)) {
|
|
return CmpLT;
|
|
} else if (f128M_eq(&op1->data.x_f128, &op2->data.x_f128)) {
|
|
return CmpEQ;
|
|
} else {
|
|
return CmpGT;
|
|
}
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
|
|
static Cmp float_cmp_zero(ConstExprValue *op) {
|
|
if (op->type->id == ZigTypeIdComptimeFloat) {
|
|
return bigfloat_cmp_zero(&op->data.x_bigfloat);
|
|
} else if (op->type->id == ZigTypeIdFloat) {
|
|
switch (op->type->data.floating.bit_count) {
|
|
case 16:
|
|
{
|
|
const float16_t zero = zig_double_to_f16(0);
|
|
if (f16_lt(op->data.x_f16, zero)) {
|
|
return CmpLT;
|
|
} else if (f16_lt(zero, op->data.x_f16)) {
|
|
return CmpGT;
|
|
} else {
|
|
return CmpEQ;
|
|
}
|
|
}
|
|
case 32:
|
|
if (op->data.x_f32 < 0.0) {
|
|
return CmpLT;
|
|
} else if (op->data.x_f32 > 0.0) {
|
|
return CmpGT;
|
|
} else {
|
|
return CmpEQ;
|
|
}
|
|
case 64:
|
|
if (op->data.x_f64 < 0.0) {
|
|
return CmpLT;
|
|
} else if (op->data.x_f64 > 0.0) {
|
|
return CmpGT;
|
|
} else {
|
|
return CmpEQ;
|
|
}
|
|
case 128:
|
|
float128_t zero_float;
|
|
ui32_to_f128M(0, &zero_float);
|
|
if (f128M_lt(&op->data.x_f128, &zero_float)) {
|
|
return CmpLT;
|
|
} else if (f128M_eq(&op->data.x_f128, &zero_float)) {
|
|
return CmpEQ;
|
|
} else {
|
|
return CmpGT;
|
|
}
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
|
|
static void float_add(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) {
|
|
assert(op1->type == op2->type);
|
|
out_val->type = op1->type;
|
|
if (op1->type->id == ZigTypeIdComptimeFloat) {
|
|
bigfloat_add(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat);
|
|
} else if (op1->type->id == ZigTypeIdFloat) {
|
|
switch (op1->type->data.floating.bit_count) {
|
|
case 16:
|
|
out_val->data.x_f16 = f16_add(op1->data.x_f16, op2->data.x_f16);
|
|
return;
|
|
case 32:
|
|
out_val->data.x_f32 = op1->data.x_f32 + op2->data.x_f32;
|
|
return;
|
|
case 64:
|
|
out_val->data.x_f64 = op1->data.x_f64 + op2->data.x_f64;
|
|
return;
|
|
case 128:
|
|
f128M_add(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128);
|
|
return;
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
|
|
static void float_sub(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) {
|
|
assert(op1->type == op2->type);
|
|
out_val->type = op1->type;
|
|
if (op1->type->id == ZigTypeIdComptimeFloat) {
|
|
bigfloat_sub(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat);
|
|
} else if (op1->type->id == ZigTypeIdFloat) {
|
|
switch (op1->type->data.floating.bit_count) {
|
|
case 16:
|
|
out_val->data.x_f16 = f16_sub(op1->data.x_f16, op2->data.x_f16);
|
|
return;
|
|
case 32:
|
|
out_val->data.x_f32 = op1->data.x_f32 - op2->data.x_f32;
|
|
return;
|
|
case 64:
|
|
out_val->data.x_f64 = op1->data.x_f64 - op2->data.x_f64;
|
|
return;
|
|
case 128:
|
|
f128M_sub(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128);
|
|
return;
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
|
|
static void float_mul(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) {
|
|
assert(op1->type == op2->type);
|
|
out_val->type = op1->type;
|
|
if (op1->type->id == ZigTypeIdComptimeFloat) {
|
|
bigfloat_mul(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat);
|
|
} else if (op1->type->id == ZigTypeIdFloat) {
|
|
switch (op1->type->data.floating.bit_count) {
|
|
case 16:
|
|
out_val->data.x_f16 = f16_mul(op1->data.x_f16, op2->data.x_f16);
|
|
return;
|
|
case 32:
|
|
out_val->data.x_f32 = op1->data.x_f32 * op2->data.x_f32;
|
|
return;
|
|
case 64:
|
|
out_val->data.x_f64 = op1->data.x_f64 * op2->data.x_f64;
|
|
return;
|
|
case 128:
|
|
f128M_mul(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128);
|
|
return;
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
|
|
static void float_div(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) {
|
|
assert(op1->type == op2->type);
|
|
out_val->type = op1->type;
|
|
if (op1->type->id == ZigTypeIdComptimeFloat) {
|
|
bigfloat_div(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat);
|
|
} else if (op1->type->id == ZigTypeIdFloat) {
|
|
switch (op1->type->data.floating.bit_count) {
|
|
case 16:
|
|
out_val->data.x_f16 = f16_div(op1->data.x_f16, op2->data.x_f16);
|
|
return;
|
|
case 32:
|
|
out_val->data.x_f32 = op1->data.x_f32 / op2->data.x_f32;
|
|
return;
|
|
case 64:
|
|
out_val->data.x_f64 = op1->data.x_f64 / op2->data.x_f64;
|
|
return;
|
|
case 128:
|
|
f128M_div(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128);
|
|
return;
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
|
|
static void float_div_trunc(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) {
|
|
assert(op1->type == op2->type);
|
|
out_val->type = op1->type;
|
|
if (op1->type->id == ZigTypeIdComptimeFloat) {
|
|
bigfloat_div_trunc(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat);
|
|
} else if (op1->type->id == ZigTypeIdFloat) {
|
|
switch (op1->type->data.floating.bit_count) {
|
|
case 16:
|
|
out_val->data.x_f16 = f16_div(op1->data.x_f16, op2->data.x_f16);
|
|
out_val->data.x_f16 = f16_roundToInt(out_val->data.x_f16, softfloat_round_minMag, false);
|
|
return;
|
|
case 32:
|
|
out_val->data.x_f32 = truncf(op1->data.x_f32 / op2->data.x_f32);
|
|
return;
|
|
case 64:
|
|
out_val->data.x_f64 = trunc(op1->data.x_f64 / op2->data.x_f64);
|
|
return;
|
|
case 128:
|
|
f128M_div(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128);
|
|
f128M_roundToInt(&out_val->data.x_f128, softfloat_round_minMag, false, &out_val->data.x_f128);
|
|
return;
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
|
|
static void float_div_floor(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) {
|
|
assert(op1->type == op2->type);
|
|
out_val->type = op1->type;
|
|
if (op1->type->id == ZigTypeIdComptimeFloat) {
|
|
bigfloat_div_floor(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat);
|
|
} else if (op1->type->id == ZigTypeIdFloat) {
|
|
switch (op1->type->data.floating.bit_count) {
|
|
case 16:
|
|
out_val->data.x_f16 = f16_div(op1->data.x_f16, op2->data.x_f16);
|
|
out_val->data.x_f16 = f16_roundToInt(out_val->data.x_f16, softfloat_round_min, false);
|
|
return;
|
|
case 32:
|
|
out_val->data.x_f32 = floorf(op1->data.x_f32 / op2->data.x_f32);
|
|
return;
|
|
case 64:
|
|
out_val->data.x_f64 = floor(op1->data.x_f64 / op2->data.x_f64);
|
|
return;
|
|
case 128:
|
|
f128M_div(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128);
|
|
f128M_roundToInt(&out_val->data.x_f128, softfloat_round_min, false, &out_val->data.x_f128);
|
|
return;
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
|
|
static void float_rem(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) {
|
|
assert(op1->type == op2->type);
|
|
out_val->type = op1->type;
|
|
if (op1->type->id == ZigTypeIdComptimeFloat) {
|
|
bigfloat_rem(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat);
|
|
} else if (op1->type->id == ZigTypeIdFloat) {
|
|
switch (op1->type->data.floating.bit_count) {
|
|
case 16:
|
|
out_val->data.x_f16 = f16_rem(op1->data.x_f16, op2->data.x_f16);
|
|
return;
|
|
case 32:
|
|
out_val->data.x_f32 = fmodf(op1->data.x_f32, op2->data.x_f32);
|
|
return;
|
|
case 64:
|
|
out_val->data.x_f64 = fmod(op1->data.x_f64, op2->data.x_f64);
|
|
return;
|
|
case 128:
|
|
f128M_rem(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128);
|
|
return;
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
|
|
// c = a - b * trunc(a / b)
|
|
static float16_t zig_f16_mod(float16_t a, float16_t b) {
|
|
float16_t c;
|
|
c = f16_div(a, b);
|
|
c = f16_roundToInt(c, softfloat_round_min, true);
|
|
c = f16_mul(b, c);
|
|
c = f16_sub(a, c);
|
|
return c;
|
|
}
|
|
|
|
// c = a - b * trunc(a / b)
|
|
static void zig_f128M_mod(const float128_t* a, const float128_t* b, float128_t* c) {
|
|
f128M_div(a, b, c);
|
|
f128M_roundToInt(c, softfloat_round_min, true, c);
|
|
f128M_mul(b, c, c);
|
|
f128M_sub(a, c, c);
|
|
}
|
|
|
|
static void float_mod(ConstExprValue *out_val, ConstExprValue *op1, ConstExprValue *op2) {
|
|
assert(op1->type == op2->type);
|
|
out_val->type = op1->type;
|
|
if (op1->type->id == ZigTypeIdComptimeFloat) {
|
|
bigfloat_mod(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat);
|
|
} else if (op1->type->id == ZigTypeIdFloat) {
|
|
switch (op1->type->data.floating.bit_count) {
|
|
case 16:
|
|
out_val->data.x_f16 = zig_f16_mod(op1->data.x_f16, op2->data.x_f16);
|
|
return;
|
|
case 32:
|
|
out_val->data.x_f32 = fmodf(fmodf(op1->data.x_f32, op2->data.x_f32) + op2->data.x_f32, op2->data.x_f32);
|
|
return;
|
|
case 64:
|
|
out_val->data.x_f64 = fmod(fmod(op1->data.x_f64, op2->data.x_f64) + op2->data.x_f64, op2->data.x_f64);
|
|
return;
|
|
case 128:
|
|
zig_f128M_mod(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128);
|
|
return;
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
|
|
static void float_negate(ConstExprValue *out_val, ConstExprValue *op) {
|
|
out_val->type = op->type;
|
|
if (op->type->id == ZigTypeIdComptimeFloat) {
|
|
bigfloat_negate(&out_val->data.x_bigfloat, &op->data.x_bigfloat);
|
|
} else if (op->type->id == ZigTypeIdFloat) {
|
|
switch (op->type->data.floating.bit_count) {
|
|
case 16:
|
|
{
|
|
const float16_t zero = zig_double_to_f16(0);
|
|
out_val->data.x_f16 = f16_sub(zero, op->data.x_f16);
|
|
return;
|
|
}
|
|
case 32:
|
|
out_val->data.x_f32 = -op->data.x_f32;
|
|
return;
|
|
case 64:
|
|
out_val->data.x_f64 = -op->data.x_f64;
|
|
return;
|
|
case 128:
|
|
float128_t zero_f128;
|
|
ui32_to_f128M(0, &zero_f128);
|
|
f128M_sub(&zero_f128, &op->data.x_f128, &out_val->data.x_f128);
|
|
return;
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
|
|
void float_write_ieee597(ConstExprValue *op, uint8_t *buf, bool is_big_endian) {
|
|
if (op->type->id == ZigTypeIdFloat) {
|
|
switch (op->type->data.floating.bit_count) {
|
|
case 16:
|
|
memcpy(buf, &op->data.x_f16, 2); // TODO wrong when compiler is big endian
|
|
return;
|
|
case 32:
|
|
memcpy(buf, &op->data.x_f32, 4); // TODO wrong when compiler is big endian
|
|
return;
|
|
case 64:
|
|
memcpy(buf, &op->data.x_f64, 8); // TODO wrong when compiler is big endian
|
|
return;
|
|
case 128:
|
|
memcpy(buf, &op->data.x_f128, 16); // TODO wrong when compiler is big endian
|
|
return;
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
|
|
void float_read_ieee597(ConstExprValue *val, uint8_t *buf, bool is_big_endian) {
|
|
if (val->type->id == ZigTypeIdFloat) {
|
|
switch (val->type->data.floating.bit_count) {
|
|
case 16:
|
|
memcpy(&val->data.x_f16, buf, 2); // TODO wrong when compiler is big endian
|
|
return;
|
|
case 32:
|
|
memcpy(&val->data.x_f32, buf, 4); // TODO wrong when compiler is big endian
|
|
return;
|
|
case 64:
|
|
memcpy(&val->data.x_f64, buf, 8); // TODO wrong when compiler is big endian
|
|
return;
|
|
case 128:
|
|
memcpy(&val->data.x_f128, buf, 16); // TODO wrong when compiler is big endian
|
|
return;
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
|
|
static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruction, ZigType *other_type,
|
|
bool explicit_cast)
|
|
{
|
|
if (type_is_invalid(other_type)) {
|
|
return false;
|
|
}
|
|
|
|
ConstExprValue *const_val = ir_resolve_const(ira, instruction, UndefBad);
|
|
assert(const_val != nullptr);
|
|
|
|
bool const_val_is_int = (const_val->type->id == ZigTypeIdInt || const_val->type->id == ZigTypeIdComptimeInt);
|
|
bool const_val_is_float = (const_val->type->id == ZigTypeIdFloat || const_val->type->id == ZigTypeIdComptimeFloat);
|
|
assert(const_val_is_int || const_val_is_float);
|
|
|
|
if (other_type->id == ZigTypeIdFloat) {
|
|
if (const_val->type->id == ZigTypeIdComptimeInt || const_val->type->id == ZigTypeIdComptimeFloat) {
|
|
return true;
|
|
}
|
|
if (const_val->type->id == ZigTypeIdInt) {
|
|
BigFloat tmp_bf;
|
|
bigfloat_init_bigint(&tmp_bf, &const_val->data.x_bigint);
|
|
BigFloat orig_bf;
|
|
switch (other_type->data.floating.bit_count) {
|
|
case 16: {
|
|
float16_t tmp = bigfloat_to_f16(&tmp_bf);
|
|
bigfloat_init_16(&orig_bf, tmp);
|
|
break;
|
|
}
|
|
case 32: {
|
|
float tmp = bigfloat_to_f32(&tmp_bf);
|
|
bigfloat_init_32(&orig_bf, tmp);
|
|
break;
|
|
}
|
|
case 64: {
|
|
double tmp = bigfloat_to_f64(&tmp_bf);
|
|
bigfloat_init_64(&orig_bf, tmp);
|
|
break;
|
|
}
|
|
case 80:
|
|
zig_panic("TODO");
|
|
case 128: {
|
|
float128_t tmp = bigfloat_to_f128(&tmp_bf);
|
|
bigfloat_init_128(&orig_bf, tmp);
|
|
break;
|
|
}
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
BigInt orig_bi;
|
|
bigint_init_bigfloat(&orig_bi, &orig_bf);
|
|
if (bigint_cmp(&orig_bi, &const_val->data.x_bigint) == CmpEQ) {
|
|
return true;
|
|
}
|
|
Buf *val_buf = buf_alloc();
|
|
bigint_append_buf(val_buf, &const_val->data.x_bigint, 10);
|
|
ir_add_error(ira, instruction,
|
|
buf_sprintf("integer value %s has no representation in type '%s'",
|
|
buf_ptr(val_buf),
|
|
buf_ptr(&other_type->name)));
|
|
return false;
|
|
}
|
|
if (other_type->data.floating.bit_count >= const_val->type->data.floating.bit_count) {
|
|
return true;
|
|
}
|
|
switch (other_type->data.floating.bit_count) {
|
|
case 16:
|
|
switch (const_val->type->data.floating.bit_count) {
|
|
case 32: {
|
|
float16_t tmp = zig_double_to_f16(const_val->data.x_f32);
|
|
float orig = zig_f16_to_double(tmp);
|
|
if (const_val->data.x_f32 == orig) {
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
case 64: {
|
|
float16_t tmp = zig_double_to_f16(const_val->data.x_f64);
|
|
double orig = zig_f16_to_double(tmp);
|
|
if (const_val->data.x_f64 == orig) {
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
case 80:
|
|
zig_panic("TODO");
|
|
case 128: {
|
|
float16_t tmp = f128M_to_f16(&const_val->data.x_f128);
|
|
float128_t orig;
|
|
f16_to_f128M(tmp, &orig);
|
|
if (f128M_eq(&orig, &const_val->data.x_f128)) {
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
break;
|
|
case 32:
|
|
switch (const_val->type->data.floating.bit_count) {
|
|
case 64: {
|
|
float tmp = const_val->data.x_f64;
|
|
double orig = tmp;
|
|
if (const_val->data.x_f64 == orig) {
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
case 80:
|
|
zig_panic("TODO");
|
|
case 128: {
|
|
float32_t tmp = f128M_to_f32(&const_val->data.x_f128);
|
|
float128_t orig;
|
|
f32_to_f128M(tmp, &orig);
|
|
if (f128M_eq(&orig, &const_val->data.x_f128)) {
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
break;
|
|
case 64:
|
|
switch (const_val->type->data.floating.bit_count) {
|
|
case 80:
|
|
zig_panic("TODO");
|
|
case 128: {
|
|
float64_t tmp = f128M_to_f64(&const_val->data.x_f128);
|
|
float128_t orig;
|
|
f64_to_f128M(tmp, &orig);
|
|
if (f128M_eq(&orig, &const_val->data.x_f128)) {
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
break;
|
|
case 80:
|
|
assert(const_val->type->data.floating.bit_count == 128);
|
|
zig_panic("TODO");
|
|
case 128:
|
|
return true;
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
Buf *val_buf = buf_alloc();
|
|
float_append_buf(val_buf, const_val);
|
|
ir_add_error(ira, instruction,
|
|
buf_sprintf("cast of value %s to type '%s' loses information",
|
|
buf_ptr(val_buf),
|
|
buf_ptr(&other_type->name)));
|
|
return false;
|
|
} else if (other_type->id == ZigTypeIdInt && const_val_is_int) {
|
|
if (!other_type->data.integral.is_signed && const_val->data.x_bigint.is_negative) {
|
|
Buf *val_buf = buf_alloc();
|
|
bigint_append_buf(val_buf, &const_val->data.x_bigint, 10);
|
|
ir_add_error(ira, instruction,
|
|
buf_sprintf("cannot cast negative value %s to unsigned integer type '%s'",
|
|
buf_ptr(val_buf),
|
|
buf_ptr(&other_type->name)));
|
|
return false;
|
|
}
|
|
if (bigint_fits_in_bits(&const_val->data.x_bigint, other_type->data.integral.bit_count,
|
|
other_type->data.integral.is_signed))
|
|
{
|
|
return true;
|
|
}
|
|
} else if (const_val_fits_in_num_lit(const_val, other_type)) {
|
|
return true;
|
|
} else if (other_type->id == ZigTypeIdOptional) {
|
|
ZigType *child_type = other_type->data.maybe.child_type;
|
|
if (const_val_fits_in_num_lit(const_val, child_type)) {
|
|
return true;
|
|
} else if (child_type->id == ZigTypeIdInt && const_val_is_int) {
|
|
if (!child_type->data.integral.is_signed && const_val->data.x_bigint.is_negative) {
|
|
Buf *val_buf = buf_alloc();
|
|
bigint_append_buf(val_buf, &const_val->data.x_bigint, 10);
|
|
ir_add_error(ira, instruction,
|
|
buf_sprintf("cannot cast negative value %s to unsigned integer type '%s'",
|
|
buf_ptr(val_buf),
|
|
buf_ptr(&child_type->name)));
|
|
return false;
|
|
}
|
|
if (bigint_fits_in_bits(&const_val->data.x_bigint,
|
|
child_type->data.integral.bit_count,
|
|
child_type->data.integral.is_signed))
|
|
{
|
|
return true;
|
|
}
|
|
} else if (child_type->id == ZigTypeIdFloat && const_val_is_float) {
|
|
return true;
|
|
}
|
|
}
|
|
if (explicit_cast && (other_type->id == ZigTypeIdInt || other_type->id == ZigTypeIdComptimeInt) &&
|
|
const_val_is_float)
|
|
{
|
|
if (float_has_fraction(const_val)) {
|
|
Buf *val_buf = buf_alloc();
|
|
float_append_buf(val_buf, const_val);
|
|
|
|
ir_add_error(ira, instruction,
|
|
buf_sprintf("fractional component prevents float value %s from being casted to type '%s'",
|
|
buf_ptr(val_buf),
|
|
buf_ptr(&other_type->name)));
|
|
return false;
|
|
} else {
|
|
if (other_type->id == ZigTypeIdComptimeInt) {
|
|
return true;
|
|
} else {
|
|
BigInt bigint;
|
|
float_init_bigint(&bigint, const_val);
|
|
if (bigint_fits_in_bits(&bigint, other_type->data.integral.bit_count,
|
|
other_type->data.integral.is_signed))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const char *num_lit_str;
|
|
Buf *val_buf = buf_alloc();
|
|
if (const_val_is_float) {
|
|
num_lit_str = "float";
|
|
float_append_buf(val_buf, const_val);
|
|
} else {
|
|
num_lit_str = "integer";
|
|
bigint_append_buf(val_buf, &const_val->data.x_bigint, 10);
|
|
}
|
|
|
|
ir_add_error(ira, instruction,
|
|
buf_sprintf("%s value %s cannot be implicitly casted to type '%s'",
|
|
num_lit_str,
|
|
buf_ptr(val_buf),
|
|
buf_ptr(&other_type->name)));
|
|
return false;
|
|
}
|
|
|
|
static bool is_tagged_union(ZigType *type) {
|
|
if (type->id != ZigTypeIdUnion)
|
|
return false;
|
|
return (type->data.unionation.decl_node->data.container_decl.auto_enum ||
|
|
type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr);
|
|
}
|
|
|
|
static void populate_error_set_table(ErrorTableEntry **errors, ZigType *set) {
|
|
assert(set->id == ZigTypeIdErrorSet);
|
|
for (uint32_t i = 0; i < set->data.error_set.err_count; i += 1) {
|
|
ErrorTableEntry *error_entry = set->data.error_set.errors[i];
|
|
assert(errors[error_entry->value] == nullptr);
|
|
errors[error_entry->value] = error_entry;
|
|
}
|
|
}
|
|
|
|
static ZigType *get_error_set_intersection(IrAnalyze *ira, ZigType *set1, ZigType *set2,
|
|
AstNode *source_node)
|
|
{
|
|
assert(set1->id == ZigTypeIdErrorSet);
|
|
assert(set2->id == ZigTypeIdErrorSet);
|
|
|
|
if (!resolve_inferred_error_set(ira->codegen, set1, source_node)) {
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
}
|
|
if (!resolve_inferred_error_set(ira->codegen, set2, source_node)) {
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
}
|
|
if (type_is_global_error_set(set1)) {
|
|
return set2;
|
|
}
|
|
if (type_is_global_error_set(set2)) {
|
|
return set1;
|
|
}
|
|
ErrorTableEntry **errors = allocate<ErrorTableEntry *>(ira->codegen->errors_by_index.length);
|
|
populate_error_set_table(errors, set1);
|
|
ZigList<ErrorTableEntry *> intersection_list = {};
|
|
|
|
ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet);
|
|
buf_resize(&err_set_type->name, 0);
|
|
buf_appendf(&err_set_type->name, "error{");
|
|
|
|
for (uint32_t i = 0; i < set2->data.error_set.err_count; i += 1) {
|
|
ErrorTableEntry *error_entry = set2->data.error_set.errors[i];
|
|
ErrorTableEntry *existing_entry = errors[error_entry->value];
|
|
if (existing_entry != nullptr) {
|
|
intersection_list.append(existing_entry);
|
|
buf_appendf(&err_set_type->name, "%s,", buf_ptr(&existing_entry->name));
|
|
}
|
|
}
|
|
free(errors);
|
|
|
|
err_set_type->data.error_set.err_count = intersection_list.length;
|
|
err_set_type->data.error_set.errors = intersection_list.items;
|
|
err_set_type->size_in_bits = ira->codegen->builtin_types.entry_global_error_set->size_in_bits;
|
|
err_set_type->abi_align = ira->codegen->builtin_types.entry_global_error_set->abi_align;
|
|
err_set_type->abi_size = ira->codegen->builtin_types.entry_global_error_set->abi_size;
|
|
|
|
buf_appendf(&err_set_type->name, "}");
|
|
|
|
return err_set_type;
|
|
}
|
|
|
|
static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted_type,
|
|
ZigType *actual_type, AstNode *source_node, bool wanted_is_mutable)
|
|
{
|
|
CodeGen *g = ira->codegen;
|
|
ConstCastOnly result = {};
|
|
result.id = ConstCastResultIdOk;
|
|
|
|
Error err;
|
|
|
|
if (wanted_type == actual_type)
|
|
return result;
|
|
|
|
// If pointers have the same representation in memory, they can be "const-casted".
|
|
// `const` attribute can be gained
|
|
// `volatile` attribute can be gained
|
|
// `allowzero` attribute can be gained (whether from explicit attribute, C pointer, or optional pointer)
|
|
// but only if !wanted_is_mutable
|
|
// alignment can be decreased
|
|
// bit offset attributes must match exactly
|
|
// PtrLenSingle/PtrLenUnknown must match exactly, but PtrLenC matches either one
|
|
ZigType *wanted_ptr_type = get_src_ptr_type(wanted_type);
|
|
ZigType *actual_ptr_type = get_src_ptr_type(actual_type);
|
|
bool wanted_allows_zero = ptr_allows_addr_zero(wanted_type);
|
|
bool actual_allows_zero = ptr_allows_addr_zero(actual_type);
|
|
bool wanted_is_c_ptr = wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenC;
|
|
bool actual_is_c_ptr = actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.ptr_len == PtrLenC;
|
|
bool wanted_opt_or_ptr = wanted_ptr_type != nullptr &&
|
|
(wanted_type->id == ZigTypeIdPointer || wanted_type->id == ZigTypeIdOptional);
|
|
bool actual_opt_or_ptr = actual_ptr_type != nullptr &&
|
|
(actual_type->id == ZigTypeIdPointer || actual_type->id == ZigTypeIdOptional);
|
|
if (wanted_opt_or_ptr && actual_opt_or_ptr) {
|
|
ConstCastOnly child = types_match_const_cast_only(ira, wanted_ptr_type->data.pointer.child_type,
|
|
actual_ptr_type->data.pointer.child_type, source_node, !wanted_ptr_type->data.pointer.is_const);
|
|
if (child.id == ConstCastResultIdInvalid)
|
|
return child;
|
|
if (child.id != ConstCastResultIdOk) {
|
|
result.id = ConstCastResultIdPointerChild;
|
|
result.data.pointer_mismatch = allocate_nonzero<ConstCastPointerMismatch>(1);
|
|
result.data.pointer_mismatch->child = child;
|
|
result.data.pointer_mismatch->wanted_child = wanted_ptr_type->data.pointer.child_type;
|
|
result.data.pointer_mismatch->actual_child = actual_ptr_type->data.pointer.child_type;
|
|
return result;
|
|
}
|
|
bool ok_allows_zero = (wanted_allows_zero &&
|
|
(actual_allows_zero || !wanted_is_mutable)) ||
|
|
(!wanted_allows_zero && !actual_allows_zero);
|
|
if (!ok_allows_zero) {
|
|
result.id = ConstCastResultIdBadAllowsZero;
|
|
result.data.bad_allows_zero = allocate_nonzero<ConstCastBadAllowsZero>(1);
|
|
result.data.bad_allows_zero->wanted_type = wanted_type;
|
|
result.data.bad_allows_zero->actual_type = actual_type;
|
|
return result;
|
|
}
|
|
if ((err = type_resolve(g, actual_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) {
|
|
result.id = ConstCastResultIdInvalid;
|
|
return result;
|
|
}
|
|
if ((err = type_resolve(g, wanted_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) {
|
|
result.id = ConstCastResultIdInvalid;
|
|
return result;
|
|
}
|
|
bool ptr_lens_equal = actual_ptr_type->data.pointer.ptr_len == wanted_ptr_type->data.pointer.ptr_len;
|
|
if ((ptr_lens_equal || wanted_is_c_ptr || actual_is_c_ptr) &&
|
|
type_has_bits(wanted_type) == type_has_bits(actual_type) &&
|
|
(!actual_ptr_type->data.pointer.is_const || wanted_ptr_type->data.pointer.is_const) &&
|
|
(!actual_ptr_type->data.pointer.is_volatile || wanted_ptr_type->data.pointer.is_volatile) &&
|
|
actual_ptr_type->data.pointer.bit_offset_in_host == wanted_ptr_type->data.pointer.bit_offset_in_host &&
|
|
actual_ptr_type->data.pointer.host_int_bytes == wanted_ptr_type->data.pointer.host_int_bytes &&
|
|
get_ptr_align(ira->codegen, actual_ptr_type) >= get_ptr_align(ira->codegen, wanted_ptr_type))
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
// slice const
|
|
if (is_slice(wanted_type) && is_slice(actual_type)) {
|
|
ZigType *actual_ptr_type = actual_type->data.structure.fields[slice_ptr_index].type_entry;
|
|
ZigType *wanted_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
|
|
if ((err = type_resolve(g, actual_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) {
|
|
result.id = ConstCastResultIdInvalid;
|
|
return result;
|
|
}
|
|
if ((err = type_resolve(g, wanted_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) {
|
|
result.id = ConstCastResultIdInvalid;
|
|
return result;
|
|
}
|
|
if ((!actual_ptr_type->data.pointer.is_const || wanted_ptr_type->data.pointer.is_const) &&
|
|
(!actual_ptr_type->data.pointer.is_volatile || wanted_ptr_type->data.pointer.is_volatile) &&
|
|
actual_ptr_type->data.pointer.bit_offset_in_host == wanted_ptr_type->data.pointer.bit_offset_in_host &&
|
|
actual_ptr_type->data.pointer.host_int_bytes == wanted_ptr_type->data.pointer.host_int_bytes &&
|
|
get_ptr_align(g, actual_ptr_type) >= get_ptr_align(g, wanted_ptr_type))
|
|
{
|
|
ConstCastOnly child = types_match_const_cast_only(ira, wanted_ptr_type->data.pointer.child_type,
|
|
actual_ptr_type->data.pointer.child_type, source_node, !wanted_ptr_type->data.pointer.is_const);
|
|
if (child.id == ConstCastResultIdInvalid)
|
|
return child;
|
|
if (child.id != ConstCastResultIdOk) {
|
|
result.id = ConstCastResultIdSliceChild;
|
|
result.data.slice_mismatch = allocate_nonzero<ConstCastSliceMismatch>(1);
|
|
result.data.slice_mismatch->child = child;
|
|
result.data.slice_mismatch->actual_child = actual_ptr_type->data.pointer.child_type;
|
|
result.data.slice_mismatch->wanted_child = wanted_ptr_type->data.pointer.child_type;
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
// maybe
|
|
if (wanted_type->id == ZigTypeIdOptional && actual_type->id == ZigTypeIdOptional) {
|
|
ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.maybe.child_type,
|
|
actual_type->data.maybe.child_type, source_node, wanted_is_mutable);
|
|
if (child.id == ConstCastResultIdInvalid)
|
|
return child;
|
|
if (child.id != ConstCastResultIdOk) {
|
|
result.id = ConstCastResultIdOptionalChild;
|
|
result.data.optional = allocate_nonzero<ConstCastOptionalMismatch>(1);
|
|
result.data.optional->child = child;
|
|
result.data.optional->wanted_child = wanted_type->data.maybe.child_type;
|
|
result.data.optional->actual_child = actual_type->data.maybe.child_type;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// error union
|
|
if (wanted_type->id == ZigTypeIdErrorUnion && actual_type->id == ZigTypeIdErrorUnion) {
|
|
ConstCastOnly payload_child = types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type,
|
|
actual_type->data.error_union.payload_type, source_node, wanted_is_mutable);
|
|
if (payload_child.id == ConstCastResultIdInvalid)
|
|
return payload_child;
|
|
if (payload_child.id != ConstCastResultIdOk) {
|
|
result.id = ConstCastResultIdErrorUnionPayload;
|
|
result.data.error_union_payload = allocate_nonzero<ConstCastErrUnionPayloadMismatch>(1);
|
|
result.data.error_union_payload->child = payload_child;
|
|
result.data.error_union_payload->wanted_payload = wanted_type->data.error_union.payload_type;
|
|
result.data.error_union_payload->actual_payload = actual_type->data.error_union.payload_type;
|
|
return result;
|
|
}
|
|
ConstCastOnly error_set_child = types_match_const_cast_only(ira, wanted_type->data.error_union.err_set_type,
|
|
actual_type->data.error_union.err_set_type, source_node, wanted_is_mutable);
|
|
if (error_set_child.id == ConstCastResultIdInvalid)
|
|
return error_set_child;
|
|
if (error_set_child.id != ConstCastResultIdOk) {
|
|
result.id = ConstCastResultIdErrorUnionErrorSet;
|
|
result.data.error_union_error_set = allocate_nonzero<ConstCastErrUnionErrSetMismatch>(1);
|
|
result.data.error_union_error_set->child = error_set_child;
|
|
result.data.error_union_error_set->wanted_err_set = wanted_type->data.error_union.err_set_type;
|
|
result.data.error_union_error_set->actual_err_set = actual_type->data.error_union.err_set_type;
|
|
return result;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// error set
|
|
if (wanted_type->id == ZigTypeIdErrorSet && actual_type->id == ZigTypeIdErrorSet) {
|
|
ZigType *contained_set = actual_type;
|
|
ZigType *container_set = wanted_type;
|
|
|
|
// if the container set is inferred, then this will always work.
|
|
if (container_set->data.error_set.infer_fn != nullptr) {
|
|
return result;
|
|
}
|
|
// if the container set is the global one, it will always work.
|
|
if (type_is_global_error_set(container_set)) {
|
|
return result;
|
|
}
|
|
|
|
if (!resolve_inferred_error_set(ira->codegen, contained_set, source_node)) {
|
|
result.id = ConstCastResultIdUnresolvedInferredErrSet;
|
|
return result;
|
|
}
|
|
|
|
if (type_is_global_error_set(contained_set)) {
|
|
result.id = ConstCastResultIdErrSetGlobal;
|
|
return result;
|
|
}
|
|
|
|
ErrorTableEntry **errors = allocate<ErrorTableEntry *>(g->errors_by_index.length);
|
|
for (uint32_t i = 0; i < container_set->data.error_set.err_count; i += 1) {
|
|
ErrorTableEntry *error_entry = container_set->data.error_set.errors[i];
|
|
assert(errors[error_entry->value] == nullptr);
|
|
errors[error_entry->value] = error_entry;
|
|
}
|
|
for (uint32_t i = 0; i < contained_set->data.error_set.err_count; i += 1) {
|
|
ErrorTableEntry *contained_error_entry = contained_set->data.error_set.errors[i];
|
|
ErrorTableEntry *error_entry = errors[contained_error_entry->value];
|
|
if (error_entry == nullptr) {
|
|
if (result.id == ConstCastResultIdOk) {
|
|
result.id = ConstCastResultIdErrSet;
|
|
result.data.error_set_mismatch = allocate<ConstCastErrSetMismatch>(1);
|
|
}
|
|
result.data.error_set_mismatch->missing_errors.append(contained_error_entry);
|
|
}
|
|
}
|
|
free(errors);
|
|
return result;
|
|
}
|
|
|
|
// fn
|
|
if (wanted_type->id == ZigTypeIdFn &&
|
|
actual_type->id == ZigTypeIdFn)
|
|
{
|
|
if (wanted_type->data.fn.fn_type_id.alignment > actual_type->data.fn.fn_type_id.alignment) {
|
|
result.id = ConstCastResultIdFnAlign;
|
|
return result;
|
|
}
|
|
if (wanted_type->data.fn.fn_type_id.cc != actual_type->data.fn.fn_type_id.cc) {
|
|
result.id = ConstCastResultIdFnCC;
|
|
return result;
|
|
}
|
|
if (wanted_type->data.fn.fn_type_id.is_var_args != actual_type->data.fn.fn_type_id.is_var_args) {
|
|
result.id = ConstCastResultIdFnVarArgs;
|
|
return result;
|
|
}
|
|
if (wanted_type->data.fn.is_generic != actual_type->data.fn.is_generic) {
|
|
result.id = ConstCastResultIdFnIsGeneric;
|
|
return result;
|
|
}
|
|
if (!wanted_type->data.fn.is_generic &&
|
|
actual_type->data.fn.fn_type_id.return_type->id != ZigTypeIdUnreachable)
|
|
{
|
|
ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.fn.fn_type_id.return_type,
|
|
actual_type->data.fn.fn_type_id.return_type, source_node, false);
|
|
if (child.id == ConstCastResultIdInvalid)
|
|
return child;
|
|
if (child.id != ConstCastResultIdOk) {
|
|
result.id = ConstCastResultIdFnReturnType;
|
|
result.data.return_type = allocate_nonzero<ConstCastOnly>(1);
|
|
*result.data.return_type = child;
|
|
return result;
|
|
}
|
|
}
|
|
if (wanted_type->data.fn.fn_type_id.param_count != actual_type->data.fn.fn_type_id.param_count) {
|
|
result.id = ConstCastResultIdFnArgCount;
|
|
return result;
|
|
}
|
|
if (wanted_type->data.fn.fn_type_id.next_param_index != actual_type->data.fn.fn_type_id.next_param_index) {
|
|
result.id = ConstCastResultIdFnGenericArgCount;
|
|
return result;
|
|
}
|
|
assert(wanted_type->data.fn.is_generic ||
|
|
wanted_type->data.fn.fn_type_id.next_param_index == wanted_type->data.fn.fn_type_id.param_count);
|
|
for (size_t i = 0; i < wanted_type->data.fn.fn_type_id.next_param_index; i += 1) {
|
|
// note it's reversed for parameters
|
|
FnTypeParamInfo *actual_param_info = &actual_type->data.fn.fn_type_id.param_info[i];
|
|
FnTypeParamInfo *expected_param_info = &wanted_type->data.fn.fn_type_id.param_info[i];
|
|
|
|
ConstCastOnly arg_child = types_match_const_cast_only(ira, actual_param_info->type,
|
|
expected_param_info->type, source_node, false);
|
|
if (arg_child.id == ConstCastResultIdInvalid)
|
|
return arg_child;
|
|
if (arg_child.id != ConstCastResultIdOk) {
|
|
result.id = ConstCastResultIdFnArg;
|
|
result.data.fn_arg.arg_index = i;
|
|
result.data.fn_arg.actual_param_type = actual_param_info->type;
|
|
result.data.fn_arg.expected_param_type = expected_param_info->type;
|
|
result.data.fn_arg.child = allocate_nonzero<ConstCastOnly>(1);
|
|
*result.data.fn_arg.child = arg_child;
|
|
return result;
|
|
}
|
|
|
|
if (expected_param_info->is_noalias != actual_param_info->is_noalias) {
|
|
result.id = ConstCastResultIdFnArgNoAlias;
|
|
result.data.arg_no_alias.arg_index = i;
|
|
return result;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
result.id = ConstCastResultIdType;
|
|
result.data.type_mismatch = allocate_nonzero<ConstCastTypeMismatch>(1);
|
|
result.data.type_mismatch->wanted_type = wanted_type;
|
|
result.data.type_mismatch->actual_type = actual_type;
|
|
return result;
|
|
}
|
|
|
|
static void update_errors_helper(CodeGen *g, ErrorTableEntry ***errors, size_t *errors_count) {
|
|
size_t old_errors_count = *errors_count;
|
|
*errors_count = g->errors_by_index.length;
|
|
*errors = reallocate(*errors, old_errors_count, *errors_count);
|
|
}
|
|
|
|
static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigType *expected_type,
|
|
IrInstruction **instructions, size_t instruction_count)
|
|
{
|
|
Error err;
|
|
assert(instruction_count >= 1);
|
|
IrInstruction *prev_inst;
|
|
size_t i = 0;
|
|
for (;;) {
|
|
prev_inst = instructions[i];
|
|
if (type_is_invalid(prev_inst->value.type)) {
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
}
|
|
if (prev_inst->value.type->id == ZigTypeIdUnreachable) {
|
|
i += 1;
|
|
if (i == instruction_count) {
|
|
return prev_inst->value.type;
|
|
}
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
ErrorTableEntry **errors = nullptr;
|
|
size_t errors_count = 0;
|
|
ZigType *err_set_type = nullptr;
|
|
if (prev_inst->value.type->id == ZigTypeIdErrorSet) {
|
|
if (!resolve_inferred_error_set(ira->codegen, prev_inst->value.type, prev_inst->source_node)) {
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
}
|
|
if (type_is_global_error_set(prev_inst->value.type)) {
|
|
err_set_type = ira->codegen->builtin_types.entry_global_error_set;
|
|
} else {
|
|
err_set_type = prev_inst->value.type;
|
|
update_errors_helper(ira->codegen, &errors, &errors_count);
|
|
|
|
for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
|
|
ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i];
|
|
assert(errors[error_entry->value] == nullptr);
|
|
errors[error_entry->value] = error_entry;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool any_are_null = (prev_inst->value.type->id == ZigTypeIdNull);
|
|
bool convert_to_const_slice = false;
|
|
for (; i < instruction_count; i += 1) {
|
|
IrInstruction *cur_inst = instructions[i];
|
|
ZigType *cur_type = cur_inst->value.type;
|
|
ZigType *prev_type = prev_inst->value.type;
|
|
|
|
if (type_is_invalid(cur_type)) {
|
|
return cur_type;
|
|
}
|
|
|
|
if (prev_type->id == ZigTypeIdUnreachable) {
|
|
prev_inst = cur_inst;
|
|
continue;
|
|
}
|
|
|
|
if (cur_type->id == ZigTypeIdUnreachable) {
|
|
continue;
|
|
}
|
|
|
|
if (prev_type->id == ZigTypeIdErrorSet) {
|
|
ir_assert(err_set_type != nullptr, prev_inst);
|
|
if (cur_type->id == ZigTypeIdErrorSet) {
|
|
if (type_is_global_error_set(err_set_type)) {
|
|
continue;
|
|
}
|
|
if (!resolve_inferred_error_set(ira->codegen, cur_type, cur_inst->source_node)) {
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
}
|
|
if (type_is_global_error_set(cur_type)) {
|
|
err_set_type = ira->codegen->builtin_types.entry_global_error_set;
|
|
prev_inst = cur_inst;
|
|
continue;
|
|
}
|
|
|
|
// number of declared errors might have increased now
|
|
update_errors_helper(ira->codegen, &errors, &errors_count);
|
|
|
|
// if err_set_type is a superset of cur_type, keep err_set_type.
|
|
// if cur_type is a superset of err_set_type, switch err_set_type to cur_type
|
|
bool prev_is_superset = true;
|
|
for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) {
|
|
ErrorTableEntry *contained_error_entry = cur_type->data.error_set.errors[i];
|
|
ErrorTableEntry *error_entry = errors[contained_error_entry->value];
|
|
if (error_entry == nullptr) {
|
|
prev_is_superset = false;
|
|
break;
|
|
}
|
|
}
|
|
if (prev_is_superset) {
|
|
continue;
|
|
}
|
|
|
|
// unset everything in errors
|
|
for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
|
|
ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i];
|
|
errors[error_entry->value] = nullptr;
|
|
}
|
|
for (uint32_t i = 0, count = ira->codegen->errors_by_index.length; i < count; i += 1) {
|
|
assert(errors[i] == nullptr);
|
|
}
|
|
for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) {
|
|
ErrorTableEntry *error_entry = cur_type->data.error_set.errors[i];
|
|
assert(errors[error_entry->value] == nullptr);
|
|
errors[error_entry->value] = error_entry;
|
|
}
|
|
bool cur_is_superset = true;
|
|
for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
|
|
ErrorTableEntry *contained_error_entry = err_set_type->data.error_set.errors[i];
|
|
ErrorTableEntry *error_entry = errors[contained_error_entry->value];
|
|
if (error_entry == nullptr) {
|
|
cur_is_superset = false;
|
|
break;
|
|
}
|
|
}
|
|
if (cur_is_superset) {
|
|
err_set_type = cur_type;
|
|
prev_inst = cur_inst;
|
|
assert(errors != nullptr);
|
|
continue;
|
|
}
|
|
|
|
// neither of them are supersets. so we invent a new error set type that is a union of both of them
|
|
err_set_type = get_error_set_union(ira->codegen, errors, cur_type, err_set_type);
|
|
assert(errors != nullptr);
|
|
continue;
|
|
} else if (cur_type->id == ZigTypeIdErrorUnion) {
|
|
if (type_is_global_error_set(err_set_type)) {
|
|
prev_inst = cur_inst;
|
|
continue;
|
|
}
|
|
ZigType *cur_err_set_type = cur_type->data.error_union.err_set_type;
|
|
if (!resolve_inferred_error_set(ira->codegen, cur_err_set_type, cur_inst->source_node)) {
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
}
|
|
if (type_is_global_error_set(cur_err_set_type)) {
|
|
err_set_type = ira->codegen->builtin_types.entry_global_error_set;
|
|
prev_inst = cur_inst;
|
|
continue;
|
|
}
|
|
|
|
update_errors_helper(ira->codegen, &errors, &errors_count);
|
|
|
|
// test if err_set_type is a subset of cur_type's error set
|
|
// unset everything in errors
|
|
for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
|
|
ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i];
|
|
errors[error_entry->value] = nullptr;
|
|
}
|
|
for (uint32_t i = 0, count = ira->codegen->errors_by_index.length; i < count; i += 1) {
|
|
assert(errors[i] == nullptr);
|
|
}
|
|
for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) {
|
|
ErrorTableEntry *error_entry = cur_err_set_type->data.error_set.errors[i];
|
|
assert(errors[error_entry->value] == nullptr);
|
|
errors[error_entry->value] = error_entry;
|
|
}
|
|
bool cur_is_superset = true;
|
|
for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
|
|
ErrorTableEntry *contained_error_entry = err_set_type->data.error_set.errors[i];
|
|
ErrorTableEntry *error_entry = errors[contained_error_entry->value];
|
|
if (error_entry == nullptr) {
|
|
cur_is_superset = false;
|
|
break;
|
|
}
|
|
}
|
|
if (cur_is_superset) {
|
|
err_set_type = cur_err_set_type;
|
|
prev_inst = cur_inst;
|
|
assert(errors != nullptr);
|
|
continue;
|
|
}
|
|
|
|
// not a subset. invent new error set type, union of both of them
|
|
err_set_type = get_error_set_union(ira->codegen, errors, cur_err_set_type, err_set_type);
|
|
prev_inst = cur_inst;
|
|
assert(errors != nullptr);
|
|
continue;
|
|
} else {
|
|
prev_inst = cur_inst;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (cur_type->id == ZigTypeIdErrorSet) {
|
|
if (prev_type->id == ZigTypeIdArray) {
|
|
convert_to_const_slice = true;
|
|
}
|
|
if (!resolve_inferred_error_set(ira->codegen, cur_type, cur_inst->source_node)) {
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
}
|
|
if (type_is_global_error_set(cur_type)) {
|
|
err_set_type = ira->codegen->builtin_types.entry_global_error_set;
|
|
continue;
|
|
}
|
|
if (err_set_type != nullptr && type_is_global_error_set(err_set_type)) {
|
|
continue;
|
|
}
|
|
|
|
update_errors_helper(ira->codegen, &errors, &errors_count);
|
|
|
|
if (err_set_type == nullptr) {
|
|
if (prev_type->id == ZigTypeIdErrorUnion) {
|
|
err_set_type = prev_type->data.error_union.err_set_type;
|
|
} else {
|
|
err_set_type = cur_type;
|
|
}
|
|
for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
|
|
ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i];
|
|
assert(errors[error_entry->value] == nullptr);
|
|
errors[error_entry->value] = error_entry;
|
|
}
|
|
if (err_set_type == cur_type) {
|
|
continue;
|
|
}
|
|
}
|
|
// check if the cur type error set is a subset
|
|
bool prev_is_superset = true;
|
|
for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) {
|
|
ErrorTableEntry *contained_error_entry = cur_type->data.error_set.errors[i];
|
|
ErrorTableEntry *error_entry = errors[contained_error_entry->value];
|
|
if (error_entry == nullptr) {
|
|
prev_is_superset = false;
|
|
break;
|
|
}
|
|
}
|
|
if (prev_is_superset) {
|
|
continue;
|
|
}
|
|
// not a subset. invent new error set type, union of both of them
|
|
err_set_type = get_error_set_union(ira->codegen, errors, err_set_type, cur_type);
|
|
assert(errors != nullptr);
|
|
continue;
|
|
}
|
|
|
|
if (prev_type->id == ZigTypeIdErrorUnion && cur_type->id == ZigTypeIdErrorUnion) {
|
|
ZigType *prev_payload_type = prev_type->data.error_union.payload_type;
|
|
ZigType *cur_payload_type = cur_type->data.error_union.payload_type;
|
|
|
|
bool const_cast_prev = types_match_const_cast_only(ira, prev_payload_type, cur_payload_type,
|
|
source_node, false).id == ConstCastResultIdOk;
|
|
bool const_cast_cur = types_match_const_cast_only(ira, cur_payload_type, prev_payload_type,
|
|
source_node, false).id == ConstCastResultIdOk;
|
|
|
|
if (const_cast_prev || const_cast_cur) {
|
|
if (const_cast_cur) {
|
|
prev_inst = cur_inst;
|
|
}
|
|
|
|
ZigType *prev_err_set_type = (err_set_type == nullptr) ? prev_type->data.error_union.err_set_type : err_set_type;
|
|
ZigType *cur_err_set_type = cur_type->data.error_union.err_set_type;
|
|
|
|
if (!resolve_inferred_error_set(ira->codegen, prev_err_set_type, cur_inst->source_node)) {
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
}
|
|
|
|
if (!resolve_inferred_error_set(ira->codegen, cur_err_set_type, cur_inst->source_node)) {
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
}
|
|
|
|
if (type_is_global_error_set(prev_err_set_type) || type_is_global_error_set(cur_err_set_type)) {
|
|
err_set_type = ira->codegen->builtin_types.entry_global_error_set;
|
|
continue;
|
|
}
|
|
|
|
update_errors_helper(ira->codegen, &errors, &errors_count);
|
|
|
|
if (err_set_type == nullptr) {
|
|
err_set_type = prev_err_set_type;
|
|
for (uint32_t i = 0; i < prev_err_set_type->data.error_set.err_count; i += 1) {
|
|
ErrorTableEntry *error_entry = prev_err_set_type->data.error_set.errors[i];
|
|
assert(errors[error_entry->value] == nullptr);
|
|
errors[error_entry->value] = error_entry;
|
|
}
|
|
}
|
|
bool prev_is_superset = true;
|
|
for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) {
|
|
ErrorTableEntry *contained_error_entry = cur_err_set_type->data.error_set.errors[i];
|
|
ErrorTableEntry *error_entry = errors[contained_error_entry->value];
|
|
if (error_entry == nullptr) {
|
|
prev_is_superset = false;
|
|
break;
|
|
}
|
|
}
|
|
if (prev_is_superset) {
|
|
continue;
|
|
}
|
|
// unset all the errors
|
|
for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
|
|
ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i];
|
|
errors[error_entry->value] = nullptr;
|
|
}
|
|
for (uint32_t i = 0, count = ira->codegen->errors_by_index.length; i < count; i += 1) {
|
|
assert(errors[i] == nullptr);
|
|
}
|
|
for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) {
|
|
ErrorTableEntry *error_entry = cur_err_set_type->data.error_set.errors[i];
|
|
assert(errors[error_entry->value] == nullptr);
|
|
errors[error_entry->value] = error_entry;
|
|
}
|
|
bool cur_is_superset = true;
|
|
for (uint32_t i = 0; i < prev_err_set_type->data.error_set.err_count; i += 1) {
|
|
ErrorTableEntry *contained_error_entry = prev_err_set_type->data.error_set.errors[i];
|
|
ErrorTableEntry *error_entry = errors[contained_error_entry->value];
|
|
if (error_entry == nullptr) {
|
|
cur_is_superset = false;
|
|
break;
|
|
}
|
|
}
|
|
if (cur_is_superset) {
|
|
err_set_type = cur_err_set_type;
|
|
continue;
|
|
}
|
|
|
|
err_set_type = get_error_set_union(ira->codegen, errors, cur_err_set_type, prev_err_set_type);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (prev_type->id == ZigTypeIdNull) {
|
|
prev_inst = cur_inst;
|
|
any_are_null = true;
|
|
continue;
|
|
}
|
|
|
|
if (cur_type->id == ZigTypeIdNull) {
|
|
any_are_null = true;
|
|
continue;
|
|
}
|
|
|
|
if (prev_type->id == ZigTypeIdEnum && cur_type->id == ZigTypeIdEnumLiteral) {
|
|
TypeEnumField *field = find_enum_type_field(prev_type, cur_inst->value.data.x_enum_literal);
|
|
if (field != nullptr) {
|
|
continue;
|
|
}
|
|
}
|
|
if (is_tagged_union(prev_type) && cur_type->id == ZigTypeIdEnumLiteral) {
|
|
TypeUnionField *field = find_union_type_field(prev_type, cur_inst->value.data.x_enum_literal);
|
|
if (field != nullptr) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (cur_type->id == ZigTypeIdEnum && prev_type->id == ZigTypeIdEnumLiteral) {
|
|
TypeEnumField *field = find_enum_type_field(cur_type, prev_inst->value.data.x_enum_literal);
|
|
if (field != nullptr) {
|
|
prev_inst = cur_inst;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (is_tagged_union(cur_type) && prev_type->id == ZigTypeIdEnumLiteral) {
|
|
TypeUnionField *field = find_union_type_field(cur_type, prev_inst->value.data.x_enum_literal);
|
|
if (field != nullptr) {
|
|
prev_inst = cur_inst;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (prev_type->id == ZigTypeIdPointer && prev_type->data.pointer.ptr_len == PtrLenC &&
|
|
(cur_type->id == ZigTypeIdComptimeInt || cur_type->id == ZigTypeIdInt))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (cur_type->id == ZigTypeIdPointer && cur_type->data.pointer.ptr_len == PtrLenC &&
|
|
(prev_type->id == ZigTypeIdComptimeInt || prev_type->id == ZigTypeIdInt))
|
|
{
|
|
prev_inst = cur_inst;
|
|
continue;
|
|
}
|
|
|
|
if (prev_type->id == ZigTypeIdPointer && cur_type->id == ZigTypeIdPointer) {
|
|
if (prev_type->data.pointer.ptr_len == PtrLenC &&
|
|
types_match_const_cast_only(ira, prev_type->data.pointer.child_type,
|
|
cur_type->data.pointer.child_type, source_node,
|
|
!prev_type->data.pointer.is_const).id == ConstCastResultIdOk)
|
|
{
|
|
continue;
|
|
}
|
|
if (cur_type->data.pointer.ptr_len == PtrLenC &&
|
|
types_match_const_cast_only(ira, cur_type->data.pointer.child_type,
|
|
prev_type->data.pointer.child_type, source_node,
|
|
!cur_type->data.pointer.is_const).id == ConstCastResultIdOk)
|
|
{
|
|
prev_inst = cur_inst;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (types_match_const_cast_only(ira, prev_type, cur_type, source_node, false).id == ConstCastResultIdOk) {
|
|
continue;
|
|
}
|
|
|
|
if (types_match_const_cast_only(ira, cur_type, prev_type, source_node, false).id == ConstCastResultIdOk) {
|
|
prev_inst = cur_inst;
|
|
continue;
|
|
}
|
|
|
|
if (prev_type->id == ZigTypeIdInt &&
|
|
cur_type->id == ZigTypeIdInt &&
|
|
prev_type->data.integral.is_signed == cur_type->data.integral.is_signed)
|
|
{
|
|
if (cur_type->data.integral.bit_count > prev_type->data.integral.bit_count) {
|
|
prev_inst = cur_inst;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (prev_type->id == ZigTypeIdFloat && cur_type->id == ZigTypeIdFloat) {
|
|
if (cur_type->data.floating.bit_count > prev_type->data.floating.bit_count) {
|
|
prev_inst = cur_inst;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (prev_type->id == ZigTypeIdErrorUnion &&
|
|
types_match_const_cast_only(ira, prev_type->data.error_union.payload_type, cur_type,
|
|
source_node, false).id == ConstCastResultIdOk)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (cur_type->id == ZigTypeIdErrorUnion &&
|
|
types_match_const_cast_only(ira, cur_type->data.error_union.payload_type, prev_type,
|
|
source_node, false).id == ConstCastResultIdOk)
|
|
{
|
|
if (err_set_type != nullptr) {
|
|
ZigType *cur_err_set_type = cur_type->data.error_union.err_set_type;
|
|
if (!resolve_inferred_error_set(ira->codegen, cur_err_set_type, cur_inst->source_node)) {
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
}
|
|
if (type_is_global_error_set(cur_err_set_type) || type_is_global_error_set(err_set_type)) {
|
|
err_set_type = ira->codegen->builtin_types.entry_global_error_set;
|
|
prev_inst = cur_inst;
|
|
continue;
|
|
}
|
|
|
|
update_errors_helper(ira->codegen, &errors, &errors_count);
|
|
|
|
err_set_type = get_error_set_union(ira->codegen, errors, err_set_type, cur_err_set_type);
|
|
}
|
|
prev_inst = cur_inst;
|
|
continue;
|
|
}
|
|
|
|
if (prev_type->id == ZigTypeIdOptional &&
|
|
types_match_const_cast_only(ira, prev_type->data.maybe.child_type, cur_type,
|
|
source_node, false).id == ConstCastResultIdOk)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (cur_type->id == ZigTypeIdOptional &&
|
|
types_match_const_cast_only(ira, cur_type->data.maybe.child_type, prev_type,
|
|
source_node, false).id == ConstCastResultIdOk)
|
|
{
|
|
prev_inst = cur_inst;
|
|
continue;
|
|
}
|
|
|
|
if (prev_type->id == ZigTypeIdOptional &&
|
|
types_match_const_cast_only(ira, cur_type, prev_type->data.maybe.child_type,
|
|
source_node, false).id == ConstCastResultIdOk)
|
|
{
|
|
prev_inst = cur_inst;
|
|
any_are_null = true;
|
|
continue;
|
|
}
|
|
|
|
if (cur_type->id == ZigTypeIdOptional &&
|
|
types_match_const_cast_only(ira, prev_type, cur_type->data.maybe.child_type,
|
|
source_node, false).id == ConstCastResultIdOk)
|
|
{
|
|
any_are_null = true;
|
|
continue;
|
|
}
|
|
|
|
if (cur_type->id == ZigTypeIdUndefined) {
|
|
continue;
|
|
}
|
|
|
|
if (prev_type->id == ZigTypeIdUndefined) {
|
|
prev_inst = cur_inst;
|
|
continue;
|
|
}
|
|
|
|
if (prev_type->id == ZigTypeIdComptimeInt ||
|
|
prev_type->id == ZigTypeIdComptimeFloat)
|
|
{
|
|
if (ir_num_lit_fits_in_other_type(ira, prev_inst, cur_type, false)) {
|
|
prev_inst = cur_inst;
|
|
continue;
|
|
} else {
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
}
|
|
}
|
|
|
|
if (cur_type->id == ZigTypeIdComptimeInt ||
|
|
cur_type->id == ZigTypeIdComptimeFloat)
|
|
{
|
|
if (ir_num_lit_fits_in_other_type(ira, cur_inst, prev_type, false)) {
|
|
continue;
|
|
} else {
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
}
|
|
}
|
|
|
|
if (cur_type->id == ZigTypeIdArray && prev_type->id == ZigTypeIdArray &&
|
|
cur_type->data.array.len != prev_type->data.array.len &&
|
|
types_match_const_cast_only(ira, cur_type->data.array.child_type, prev_type->data.array.child_type,
|
|
source_node, false).id == ConstCastResultIdOk)
|
|
{
|
|
convert_to_const_slice = true;
|
|
prev_inst = cur_inst;
|
|
continue;
|
|
}
|
|
|
|
if (cur_type->id == ZigTypeIdArray && prev_type->id == ZigTypeIdArray &&
|
|
cur_type->data.array.len != prev_type->data.array.len &&
|
|
types_match_const_cast_only(ira, prev_type->data.array.child_type, cur_type->data.array.child_type,
|
|
source_node, false).id == ConstCastResultIdOk)
|
|
{
|
|
convert_to_const_slice = true;
|
|
continue;
|
|
}
|
|
|
|
if (cur_type->id == ZigTypeIdArray && is_slice(prev_type) &&
|
|
(prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const ||
|
|
cur_type->data.array.len == 0) &&
|
|
types_match_const_cast_only(ira,
|
|
prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type,
|
|
cur_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk)
|
|
{
|
|
convert_to_const_slice = false;
|
|
continue;
|
|
}
|
|
|
|
if (prev_type->id == ZigTypeIdArray && is_slice(cur_type) &&
|
|
(cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const ||
|
|
prev_type->data.array.len == 0) &&
|
|
types_match_const_cast_only(ira,
|
|
cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type,
|
|
prev_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk)
|
|
{
|
|
prev_inst = cur_inst;
|
|
convert_to_const_slice = false;
|
|
continue;
|
|
}
|
|
|
|
if (prev_type->id == ZigTypeIdEnum && cur_type->id == ZigTypeIdUnion &&
|
|
(cur_type->data.unionation.decl_node->data.container_decl.auto_enum || cur_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))
|
|
{
|
|
if ((err = type_resolve(ira->codegen, cur_type, ResolveStatusZeroBitsKnown)))
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
if (cur_type->data.unionation.tag_type == prev_type) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (cur_type->id == ZigTypeIdEnum && prev_type->id == ZigTypeIdUnion &&
|
|
(prev_type->data.unionation.decl_node->data.container_decl.auto_enum || prev_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))
|
|
{
|
|
if ((err = type_resolve(ira->codegen, prev_type, ResolveStatusZeroBitsKnown)))
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
if (prev_type->data.unionation.tag_type == cur_type) {
|
|
prev_inst = cur_inst;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
ErrorMsg *msg = ir_add_error_node(ira, source_node,
|
|
buf_sprintf("incompatible types: '%s' and '%s'",
|
|
buf_ptr(&prev_type->name), buf_ptr(&cur_type->name)));
|
|
add_error_note(ira->codegen, msg, prev_inst->source_node,
|
|
buf_sprintf("type '%s' here", buf_ptr(&prev_type->name)));
|
|
add_error_note(ira->codegen, msg, cur_inst->source_node,
|
|
buf_sprintf("type '%s' here", buf_ptr(&cur_type->name)));
|
|
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
}
|
|
|
|
free(errors);
|
|
|
|
if (convert_to_const_slice) {
|
|
assert(prev_inst->value.type->id == ZigTypeIdArray);
|
|
ZigType *ptr_type = get_pointer_to_type_extra(
|
|
ira->codegen, prev_inst->value.type->data.array.child_type,
|
|
true, false, PtrLenUnknown,
|
|
0, 0, 0, false);
|
|
ZigType *slice_type = get_slice_type(ira->codegen, ptr_type);
|
|
if (err_set_type != nullptr) {
|
|
return get_error_union_type(ira->codegen, err_set_type, slice_type);
|
|
} else {
|
|
return slice_type;
|
|
}
|
|
} else if (err_set_type != nullptr) {
|
|
if (prev_inst->value.type->id == ZigTypeIdErrorSet) {
|
|
return err_set_type;
|
|
} else if (prev_inst->value.type->id == ZigTypeIdErrorUnion) {
|
|
ZigType *payload_type = prev_inst->value.type->data.error_union.payload_type;
|
|
if ((err = type_resolve(ira->codegen, payload_type, ResolveStatusSizeKnown)))
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
return get_error_union_type(ira->codegen, err_set_type, payload_type);
|
|
} else if (expected_type != nullptr && expected_type->id == ZigTypeIdErrorUnion) {
|
|
ZigType *payload_type = expected_type->data.error_union.payload_type;
|
|
if ((err = type_resolve(ira->codegen, payload_type, ResolveStatusSizeKnown)))
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
return get_error_union_type(ira->codegen, err_set_type, payload_type);
|
|
} else {
|
|
if (prev_inst->value.type->id == ZigTypeIdComptimeInt ||
|
|
prev_inst->value.type->id == ZigTypeIdComptimeFloat)
|
|
{
|
|
ir_add_error_node(ira, source_node,
|
|
buf_sprintf("unable to make error union out of number literal"));
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
} else if (prev_inst->value.type->id == ZigTypeIdNull) {
|
|
ir_add_error_node(ira, source_node,
|
|
buf_sprintf("unable to make error union out of null literal"));
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
} else {
|
|
if ((err = type_resolve(ira->codegen, prev_inst->value.type, ResolveStatusSizeKnown)))
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type);
|
|
}
|
|
}
|
|
} else if (any_are_null && prev_inst->value.type->id != ZigTypeIdNull) {
|
|
if (prev_inst->value.type->id == ZigTypeIdComptimeInt ||
|
|
prev_inst->value.type->id == ZigTypeIdComptimeFloat)
|
|
{
|
|
ir_add_error_node(ira, source_node,
|
|
buf_sprintf("unable to make maybe out of number literal"));
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
} else if (prev_inst->value.type->id == ZigTypeIdOptional) {
|
|
return prev_inst->value.type;
|
|
} else {
|
|
if ((err = type_resolve(ira->codegen, prev_inst->value.type, ResolveStatusSizeKnown)))
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
return get_optional_type(ira->codegen, prev_inst->value.type);
|
|
}
|
|
} else {
|
|
return prev_inst->value.type;
|
|
}
|
|
}
|
|
|
|
static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_global_refs) {
|
|
ConstGlobalRefs *global_refs = dest->global_refs;
|
|
memcpy(dest, src, sizeof(ConstExprValue));
|
|
if (!same_global_refs) {
|
|
dest->global_refs = global_refs;
|
|
if (src->special == ConstValSpecialUndef)
|
|
return;
|
|
if (dest->type->id == ZigTypeIdStruct) {
|
|
dest->data.x_struct.fields = create_const_vals(dest->type->data.structure.src_field_count);
|
|
for (size_t i = 0; i < dest->type->data.structure.src_field_count; i += 1) {
|
|
copy_const_val(&dest->data.x_struct.fields[i], &src->data.x_struct.fields[i], false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInstruction *source_instr,
|
|
CastOp cast_op,
|
|
ConstExprValue *other_val, ZigType *other_type,
|
|
ConstExprValue *const_val, ZigType *new_type)
|
|
{
|
|
const_val->special = other_val->special;
|
|
|
|
assert(other_val != const_val);
|
|
switch (cast_op) {
|
|
case CastOpNoCast:
|
|
zig_unreachable();
|
|
case CastOpErrSet:
|
|
case CastOpBitCast:
|
|
zig_panic("TODO");
|
|
case CastOpNoop:
|
|
{
|
|
bool same_global_refs = other_val->special == ConstValSpecialStatic;
|
|
copy_const_val(const_val, other_val, same_global_refs);
|
|
const_val->type = new_type;
|
|
break;
|
|
}
|
|
case CastOpNumLitToConcrete:
|
|
if (other_val->type->id == ZigTypeIdComptimeFloat) {
|
|
assert(new_type->id == ZigTypeIdFloat);
|
|
switch (new_type->data.floating.bit_count) {
|
|
case 16:
|
|
const_val->data.x_f16 = bigfloat_to_f16(&other_val->data.x_bigfloat);
|
|
break;
|
|
case 32:
|
|
const_val->data.x_f32 = bigfloat_to_f32(&other_val->data.x_bigfloat);
|
|
break;
|
|
case 64:
|
|
const_val->data.x_f64 = bigfloat_to_f64(&other_val->data.x_bigfloat);
|
|
break;
|
|
case 80:
|
|
zig_panic("TODO");
|
|
case 128:
|
|
const_val->data.x_f128 = bigfloat_to_f128(&other_val->data.x_bigfloat);
|
|
break;
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
} else if (other_val->type->id == ZigTypeIdComptimeInt) {
|
|
bigint_init_bigint(&const_val->data.x_bigint, &other_val->data.x_bigint);
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
const_val->type = new_type;
|
|
break;
|
|
case CastOpIntToFloat:
|
|
{
|
|
assert(new_type->id == ZigTypeIdFloat);
|
|
|
|
BigFloat bigfloat;
|
|
bigfloat_init_bigint(&bigfloat, &other_val->data.x_bigint);
|
|
switch (new_type->data.floating.bit_count) {
|
|
case 16:
|
|
const_val->data.x_f16 = bigfloat_to_f16(&bigfloat);
|
|
break;
|
|
case 32:
|
|
const_val->data.x_f32 = bigfloat_to_f32(&bigfloat);
|
|
break;
|
|
case 64:
|
|
const_val->data.x_f64 = bigfloat_to_f64(&bigfloat);
|
|
break;
|
|
case 80:
|
|
zig_panic("TODO");
|
|
case 128:
|
|
const_val->data.x_f128 = bigfloat_to_f128(&bigfloat);
|
|
break;
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
const_val->special = ConstValSpecialStatic;
|
|
break;
|
|
}
|
|
case CastOpFloatToInt:
|
|
float_init_bigint(&const_val->data.x_bigint, other_val);
|
|
if (new_type->id == ZigTypeIdInt) {
|
|
if (!bigint_fits_in_bits(&const_val->data.x_bigint, new_type->data.integral.bit_count,
|
|
new_type->data.integral.is_signed))
|
|
{
|
|
Buf *int_buf = buf_alloc();
|
|
bigint_append_buf(int_buf, &const_val->data.x_bigint, 10);
|
|
|
|
ir_add_error(ira, source_instr,
|
|
buf_sprintf("integer value '%s' cannot be stored in type '%s'",
|
|
buf_ptr(int_buf), buf_ptr(&new_type->name)));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const_val->special = ConstValSpecialStatic;
|
|
break;
|
|
case CastOpBoolToInt:
|
|
bigint_init_unsigned(&const_val->data.x_bigint, other_val->data.x_bool ? 1 : 0);
|
|
const_val->special = ConstValSpecialStatic;
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static IrInstruction *ir_const(IrAnalyze *ira, IrInstruction *old_instruction, ZigType *ty) {
|
|
IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb,
|
|
old_instruction->scope, old_instruction->source_node);
|
|
IrInstruction *new_instruction = &const_instruction->base;
|
|
new_instruction->value.type = ty;
|
|
new_instruction->value.special = ConstValSpecialStatic;
|
|
return new_instruction;
|
|
}
|
|
|
|
static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value,
|
|
ZigType *wanted_type, CastOp cast_op)
|
|
{
|
|
if (instr_is_comptime(value) || !type_has_bits(wanted_type)) {
|
|
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
|
|
if (!eval_const_expr_implicit_cast(ira, source_instr, cast_op, &value->value, value->value.type,
|
|
&result->value, wanted_type))
|
|
{
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
return result;
|
|
} else {
|
|
IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type, value, cast_op);
|
|
result->value.type = wanted_type;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
static IrInstruction *ir_resolve_ptr_of_array_to_unknown_len_ptr(IrAnalyze *ira, IrInstruction *source_instr,
|
|
IrInstruction *value, ZigType *wanted_type)
|
|
{
|
|
assert(value->value.type->id == ZigTypeIdPointer);
|
|
|
|
Error err;
|
|
|
|
if ((err = type_resolve(ira->codegen, value->value.type->data.pointer.child_type,
|
|
ResolveStatusAlignmentKnown)))
|
|
{
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
wanted_type = adjust_ptr_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, value->value.type));
|
|
|
|
if (instr_is_comptime(value)) {
|
|
ConstExprValue *pointee = const_ptr_pointee(ira, ira->codegen, &value->value, source_instr->source_node);
|
|
if (pointee == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
if (pointee->special != ConstValSpecialRuntime) {
|
|
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
|
|
result->value.data.x_ptr.special = ConstPtrSpecialBaseArray;
|
|
result->value.data.x_ptr.mut = value->value.data.x_ptr.mut;
|
|
result->value.data.x_ptr.data.base_array.array_val = pointee;
|
|
result->value.data.x_ptr.data.base_array.elem_index = 0;
|
|
result->value.data.x_ptr.data.base_array.is_cstr = false;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node,
|
|
wanted_type, value, CastOpBitCast);
|
|
result->value.type = wanted_type;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInstruction *source_instr,
|
|
IrInstruction *value, ZigType *wanted_type, ResultLoc *result_loc)
|
|
{
|
|
Error err;
|
|
|
|
if ((err = type_resolve(ira->codegen, value->value.type->data.pointer.child_type,
|
|
ResolveStatusAlignmentKnown)))
|
|
{
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
wanted_type = adjust_slice_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, value->value.type));
|
|
|
|
if (instr_is_comptime(value)) {
|
|
ConstExprValue *pointee = const_ptr_pointee(ira, ira->codegen, &value->value, source_instr->source_node);
|
|
if (pointee == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
if (pointee->special != ConstValSpecialRuntime) {
|
|
assert(value->value.type->id == ZigTypeIdPointer);
|
|
ZigType *array_type = value->value.type->data.pointer.child_type;
|
|
assert(is_slice(wanted_type));
|
|
bool is_const = wanted_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const;
|
|
|
|
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
|
|
init_const_slice(ira->codegen, &result->value, pointee, 0, array_type->data.array.len, is_const);
|
|
result->value.data.x_struct.fields[slice_ptr_index].data.x_ptr.mut =
|
|
value->value.data.x_ptr.mut;
|
|
result->value.type = wanted_type;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (result_loc == nullptr) result_loc = no_result_loc();
|
|
IrInstruction *result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, true,
|
|
false, true);
|
|
if (type_is_invalid(result_loc_inst->value.type) || instr_is_unreachable(result_loc_inst)) {
|
|
return result_loc_inst;
|
|
}
|
|
return ir_build_ptr_of_array_to_slice(ira, source_instr, wanted_type, value, result_loc_inst);
|
|
}
|
|
|
|
static IrBasicBlock *ir_get_new_bb(IrAnalyze *ira, IrBasicBlock *old_bb, IrInstruction *ref_old_instruction) {
|
|
assert(old_bb);
|
|
|
|
if (old_bb->other) {
|
|
if (ref_old_instruction == nullptr || old_bb->other->ref_instruction != ref_old_instruction) {
|
|
return old_bb->other;
|
|
}
|
|
}
|
|
|
|
IrBasicBlock *new_bb = ir_build_bb_from(&ira->new_irb, old_bb);
|
|
new_bb->ref_instruction = ref_old_instruction;
|
|
|
|
return new_bb;
|
|
}
|
|
|
|
static IrBasicBlock *ir_get_new_bb_runtime(IrAnalyze *ira, IrBasicBlock *old_bb, IrInstruction *ref_old_instruction) {
|
|
assert(ref_old_instruction != nullptr);
|
|
IrBasicBlock *new_bb = ir_get_new_bb(ira, old_bb, ref_old_instruction);
|
|
if (new_bb->must_be_comptime_source_instr) {
|
|
ErrorMsg *msg = ir_add_error(ira, ref_old_instruction,
|
|
buf_sprintf("control flow attempts to use compile-time variable at runtime"));
|
|
add_error_note(ira->codegen, msg, new_bb->must_be_comptime_source_instr->source_node,
|
|
buf_sprintf("compile-time variable assigned here"));
|
|
return nullptr;
|
|
}
|
|
return new_bb;
|
|
}
|
|
|
|
static void ir_start_bb(IrAnalyze *ira, IrBasicBlock *old_bb, IrBasicBlock *const_predecessor_bb) {
|
|
ir_assert(!old_bb->suspended, (old_bb->instruction_list.length != 0) ? old_bb->instruction_list.at(0) : nullptr);
|
|
ira->instruction_index = 0;
|
|
ira->old_irb.current_basic_block = old_bb;
|
|
ira->const_predecessor_bb = const_predecessor_bb;
|
|
ira->old_bb_index = old_bb->index;
|
|
}
|
|
|
|
static IrInstruction *ira_suspend(IrAnalyze *ira, IrInstruction *old_instruction, IrBasicBlock *next_bb,
|
|
IrSuspendPosition *suspend_pos)
|
|
{
|
|
if (ira->codegen->verbose_ir) {
|
|
fprintf(stderr, "suspend %s_%zu %s_%zu #%zu (%zu,%zu)\n", ira->old_irb.current_basic_block->name_hint,
|
|
ira->old_irb.current_basic_block->debug_id,
|
|
ira->old_irb.exec->basic_block_list.at(ira->old_bb_index)->name_hint,
|
|
ira->old_irb.exec->basic_block_list.at(ira->old_bb_index)->debug_id,
|
|
ira->old_irb.current_basic_block->instruction_list.at(ira->instruction_index)->debug_id,
|
|
ira->old_bb_index, ira->instruction_index);
|
|
}
|
|
suspend_pos->basic_block_index = ira->old_bb_index;
|
|
suspend_pos->instruction_index = ira->instruction_index;
|
|
|
|
ira->old_irb.current_basic_block->suspended = true;
|
|
|
|
// null next_bb means that the caller plans to call ira_resume before returning
|
|
if (next_bb != nullptr) {
|
|
ira->old_bb_index = next_bb->index;
|
|
ira->old_irb.current_basic_block = ira->old_irb.exec->basic_block_list.at(ira->old_bb_index);
|
|
assert(ira->old_irb.current_basic_block == next_bb);
|
|
ira->instruction_index = 0;
|
|
ira->const_predecessor_bb = nullptr;
|
|
next_bb->other = ir_get_new_bb_runtime(ira, next_bb, old_instruction);
|
|
ira->new_irb.current_basic_block = next_bb->other;
|
|
}
|
|
return ira->codegen->unreach_instruction;
|
|
}
|
|
|
|
static IrInstruction *ira_resume(IrAnalyze *ira) {
|
|
IrSuspendPosition pos = ira->resume_stack.pop();
|
|
if (ira->codegen->verbose_ir) {
|
|
fprintf(stderr, "resume (%zu,%zu) ", pos.basic_block_index, pos.instruction_index);
|
|
}
|
|
ira->old_bb_index = pos.basic_block_index;
|
|
ira->old_irb.current_basic_block = ira->old_irb.exec->basic_block_list.at(ira->old_bb_index);
|
|
assert(ira->old_irb.current_basic_block->in_resume_stack);
|
|
ira->old_irb.current_basic_block->in_resume_stack = false;
|
|
ira->old_irb.current_basic_block->suspended = false;
|
|
ira->instruction_index = pos.instruction_index;
|
|
assert(pos.instruction_index < ira->old_irb.current_basic_block->instruction_list.length);
|
|
if (ira->codegen->verbose_ir) {
|
|
fprintf(stderr, "%s_%zu #%zu\n", ira->old_irb.current_basic_block->name_hint,
|
|
ira->old_irb.current_basic_block->debug_id,
|
|
ira->old_irb.current_basic_block->instruction_list.at(pos.instruction_index)->debug_id);
|
|
}
|
|
ira->const_predecessor_bb = nullptr;
|
|
ira->new_irb.current_basic_block = ira->old_irb.current_basic_block->other;
|
|
assert(ira->new_irb.current_basic_block != nullptr);
|
|
return ira->codegen->unreach_instruction;
|
|
}
|
|
|
|
static void ir_start_next_bb(IrAnalyze *ira) {
|
|
ira->old_bb_index += 1;
|
|
|
|
bool need_repeat = true;
|
|
for (;;) {
|
|
while (ira->old_bb_index < ira->old_irb.exec->basic_block_list.length) {
|
|
IrBasicBlock *old_bb = ira->old_irb.exec->basic_block_list.at(ira->old_bb_index);
|
|
if (old_bb->other == nullptr && old_bb->suspend_instruction_ref == nullptr) {
|
|
ira->old_bb_index += 1;
|
|
continue;
|
|
}
|
|
// if it's already started, or
|
|
// if it's a suspended block,
|
|
// then skip it
|
|
if (old_bb->suspended ||
|
|
(old_bb->other != nullptr && old_bb->other->instruction_list.length != 0) ||
|
|
(old_bb->other != nullptr && old_bb->other->already_appended))
|
|
{
|
|
ira->old_bb_index += 1;
|
|
continue;
|
|
}
|
|
|
|
// if there is a resume_stack, pop one from there rather than moving on.
|
|
// the last item of the resume stack will be a basic block that will
|
|
// move on to the next one below
|
|
if (ira->resume_stack.length != 0) {
|
|
ira_resume(ira);
|
|
return;
|
|
}
|
|
|
|
if (old_bb->other == nullptr) {
|
|
old_bb->other = ir_get_new_bb_runtime(ira, old_bb, old_bb->suspend_instruction_ref);
|
|
}
|
|
ira->new_irb.current_basic_block = old_bb->other;
|
|
ir_start_bb(ira, old_bb, nullptr);
|
|
return;
|
|
}
|
|
if (!need_repeat) {
|
|
if (ira->resume_stack.length != 0) {
|
|
ira_resume(ira);
|
|
}
|
|
return;
|
|
}
|
|
need_repeat = false;
|
|
ira->old_bb_index = 0;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
static void ir_finish_bb(IrAnalyze *ira) {
|
|
if (!ira->new_irb.current_basic_block->already_appended) {
|
|
ira->new_irb.current_basic_block->already_appended = true;
|
|
if (ira->codegen->verbose_ir) {
|
|
fprintf(stderr, "append new bb %s_%zu\n", ira->new_irb.current_basic_block->name_hint,
|
|
ira->new_irb.current_basic_block->debug_id);
|
|
}
|
|
ira->new_irb.exec->basic_block_list.append(ira->new_irb.current_basic_block);
|
|
}
|
|
ira->instruction_index += 1;
|
|
while (ira->instruction_index < ira->old_irb.current_basic_block->instruction_list.length) {
|
|
IrInstruction *next_instruction = ira->old_irb.current_basic_block->instruction_list.at(ira->instruction_index);
|
|
if (!next_instruction->is_gen) {
|
|
ir_add_error(ira, next_instruction, buf_sprintf("unreachable code"));
|
|
break;
|
|
}
|
|
ira->instruction_index += 1;
|
|
}
|
|
|
|
ir_start_next_bb(ira);
|
|
}
|
|
|
|
static IrInstruction *ir_unreach_error(IrAnalyze *ira) {
|
|
ira->old_bb_index = SIZE_MAX;
|
|
ira->new_irb.exec->invalid = true;
|
|
return ira->codegen->unreach_instruction;
|
|
}
|
|
|
|
static bool ir_emit_backward_branch(IrAnalyze *ira, IrInstruction *source_instruction) {
|
|
size_t *bbc = ira->new_irb.exec->backward_branch_count;
|
|
size_t *quota = ira->new_irb.exec->backward_branch_quota;
|
|
|
|
// If we're already over quota, we've already given an error message for this.
|
|
if (*bbc > *quota) {
|
|
assert(ira->codegen->errors.length > 0);
|
|
return false;
|
|
}
|
|
|
|
*bbc += 1;
|
|
if (*bbc > *quota) {
|
|
ir_add_error(ira, source_instruction,
|
|
buf_sprintf("evaluation exceeded %" ZIG_PRI_usize " backwards branches", *quota));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static IrInstruction *ir_inline_bb(IrAnalyze *ira, IrInstruction *source_instruction, IrBasicBlock *old_bb) {
|
|
if (old_bb->debug_id <= ira->old_irb.current_basic_block->debug_id) {
|
|
if (!ir_emit_backward_branch(ira, source_instruction))
|
|
return ir_unreach_error(ira);
|
|
}
|
|
|
|
old_bb->other = ira->old_irb.current_basic_block->other;
|
|
ir_start_bb(ira, old_bb, ira->old_irb.current_basic_block);
|
|
return ira->codegen->unreach_instruction;
|
|
}
|
|
|
|
static IrInstruction *ir_finish_anal(IrAnalyze *ira, IrInstruction *instruction) {
|
|
if (instruction->value.type->id == ZigTypeIdUnreachable)
|
|
ir_finish_bb(ira);
|
|
return instruction;
|
|
}
|
|
|
|
static IrInstruction *ir_const_type(IrAnalyze *ira, IrInstruction *source_instruction, ZigType *ty) {
|
|
IrInstruction *result = ir_const(ira, source_instruction, ira->codegen->builtin_types.entry_type);
|
|
result->value.data.x_type = ty;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_const_bool(IrAnalyze *ira, IrInstruction *source_instruction, bool value) {
|
|
IrInstruction *result = ir_const(ira, source_instruction, ira->codegen->builtin_types.entry_bool);
|
|
result->value.data.x_bool = value;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_const_undef(IrAnalyze *ira, IrInstruction *source_instruction, ZigType *ty) {
|
|
IrInstruction *result = ir_const(ira, source_instruction, ty);
|
|
result->value.special = ConstValSpecialUndef;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_const_unreachable(IrAnalyze *ira, IrInstruction *source_instruction) {
|
|
IrInstruction *result = ir_const(ira, source_instruction, ira->codegen->builtin_types.entry_unreachable);
|
|
result->value.special = ConstValSpecialStatic;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_const_void(IrAnalyze *ira, IrInstruction *source_instruction) {
|
|
return ir_const(ira, source_instruction, ira->codegen->builtin_types.entry_void);
|
|
}
|
|
|
|
static IrInstruction *ir_const_unsigned(IrAnalyze *ira, IrInstruction *source_instruction, uint64_t value) {
|
|
IrInstruction *result = ir_const(ira, source_instruction, ira->codegen->builtin_types.entry_num_lit_int);
|
|
bigint_init_unsigned(&result->value.data.x_bigint, value);
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_get_const_ptr(IrAnalyze *ira, IrInstruction *instruction,
|
|
ConstExprValue *pointee, ZigType *pointee_type,
|
|
ConstPtrMut ptr_mut, bool ptr_is_const, bool ptr_is_volatile, uint32_t ptr_align)
|
|
{
|
|
ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, pointee_type,
|
|
ptr_is_const, ptr_is_volatile, PtrLenSingle, ptr_align, 0, 0, false);
|
|
IrInstruction *const_instr = ir_const(ira, instruction, ptr_type);
|
|
ConstExprValue *const_val = &const_instr->value;
|
|
const_val->data.x_ptr.special = ConstPtrSpecialRef;
|
|
const_val->data.x_ptr.mut = ptr_mut;
|
|
const_val->data.x_ptr.data.ref.pointee = pointee;
|
|
return const_instr;
|
|
}
|
|
|
|
static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, UndefAllowed undef_allowed) {
|
|
switch (value->value.special) {
|
|
case ConstValSpecialStatic:
|
|
return &value->value;
|
|
case ConstValSpecialRuntime:
|
|
if (!type_has_bits(value->value.type)) {
|
|
return &value->value;
|
|
}
|
|
ir_add_error(ira, value, buf_sprintf("unable to evaluate constant expression"));
|
|
return nullptr;
|
|
case ConstValSpecialUndef:
|
|
if (undef_allowed == UndefOk) {
|
|
return &value->value;
|
|
} else {
|
|
ir_add_error(ira, value, buf_sprintf("use of undefined value here causes undefined behavior"));
|
|
return nullptr;
|
|
}
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node,
|
|
ZigType *expected_type, size_t *backward_branch_count, size_t *backward_branch_quota,
|
|
ZigFn *fn_entry, Buf *c_import_buf, AstNode *source_node, Buf *exec_name,
|
|
IrExecutable *parent_exec, AstNode *expected_type_source_node)
|
|
{
|
|
if (expected_type != nullptr && type_is_invalid(expected_type))
|
|
return &codegen->invalid_instruction->value;
|
|
|
|
IrExecutable *ir_executable = allocate<IrExecutable>(1);
|
|
ir_executable->source_node = source_node;
|
|
ir_executable->parent_exec = parent_exec;
|
|
ir_executable->name = exec_name;
|
|
ir_executable->is_inline = true;
|
|
ir_executable->fn_entry = fn_entry;
|
|
ir_executable->c_import_buf = c_import_buf;
|
|
ir_executable->begin_scope = scope;
|
|
ir_gen(codegen, node, scope, ir_executable);
|
|
|
|
if (ir_executable->invalid)
|
|
return &codegen->invalid_instruction->value;
|
|
|
|
if (codegen->verbose_ir) {
|
|
fprintf(stderr, "\nSource: ");
|
|
ast_render(stderr, node, 4);
|
|
fprintf(stderr, "\n{ // (IR)\n");
|
|
ir_print(codegen, stderr, ir_executable, 2);
|
|
fprintf(stderr, "}\n");
|
|
}
|
|
IrExecutable *analyzed_executable = allocate<IrExecutable>(1);
|
|
analyzed_executable->source_node = source_node;
|
|
analyzed_executable->parent_exec = parent_exec;
|
|
analyzed_executable->source_exec = ir_executable;
|
|
analyzed_executable->name = exec_name;
|
|
analyzed_executable->is_inline = true;
|
|
analyzed_executable->fn_entry = fn_entry;
|
|
analyzed_executable->c_import_buf = c_import_buf;
|
|
analyzed_executable->backward_branch_count = backward_branch_count;
|
|
analyzed_executable->backward_branch_quota = backward_branch_quota;
|
|
analyzed_executable->begin_scope = scope;
|
|
ZigType *result_type = ir_analyze(codegen, ir_executable, analyzed_executable, expected_type, expected_type_source_node);
|
|
if (type_is_invalid(result_type))
|
|
return &codegen->invalid_instruction->value;
|
|
|
|
if (codegen->verbose_ir) {
|
|
fprintf(stderr, "{ // (analyzed)\n");
|
|
ir_print(codegen, stderr, analyzed_executable, 2);
|
|
fprintf(stderr, "}\n");
|
|
}
|
|
|
|
return ir_exec_const_result(codegen, analyzed_executable);
|
|
}
|
|
|
|
static ErrorTableEntry *ir_resolve_error(IrAnalyze *ira, IrInstruction *err_value) {
|
|
if (type_is_invalid(err_value->value.type))
|
|
return nullptr;
|
|
|
|
if (err_value->value.type->id != ZigTypeIdErrorSet) {
|
|
ir_add_error(ira, err_value,
|
|
buf_sprintf("expected error, found '%s'", buf_ptr(&err_value->value.type->name)));
|
|
return nullptr;
|
|
}
|
|
|
|
ConstExprValue *const_val = ir_resolve_const(ira, err_value, UndefBad);
|
|
if (!const_val)
|
|
return nullptr;
|
|
|
|
assert(const_val->data.x_err_set != nullptr);
|
|
return const_val->data.x_err_set;
|
|
}
|
|
|
|
static ZigType *ir_resolve_type(IrAnalyze *ira, IrInstruction *type_value) {
|
|
if (type_is_invalid(type_value->value.type))
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
|
|
if (type_value->value.type->id != ZigTypeIdMetaType) {
|
|
ir_add_error(ira, type_value,
|
|
buf_sprintf("expected type 'type', found '%s'", buf_ptr(&type_value->value.type->name)));
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
}
|
|
|
|
ConstExprValue *const_val = ir_resolve_const(ira, type_value, UndefBad);
|
|
if (!const_val)
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
|
|
assert(const_val->data.x_type != nullptr);
|
|
return const_val->data.x_type;
|
|
}
|
|
|
|
static ZigType *ir_resolve_int_type(IrAnalyze *ira, IrInstruction *type_value) {
|
|
ZigType *ty = ir_resolve_type(ira, type_value);
|
|
if (type_is_invalid(ty))
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
|
|
if (ty->id != ZigTypeIdInt) {
|
|
ir_add_error(ira, type_value,
|
|
buf_sprintf("expected integer type, found '%s'", buf_ptr(&ty->name)));
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
}
|
|
|
|
return ty;
|
|
}
|
|
|
|
static ZigType *ir_resolve_error_set_type(IrAnalyze *ira, IrInstruction *op_source, IrInstruction *type_value) {
|
|
if (type_is_invalid(type_value->value.type))
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
|
|
if (type_value->value.type->id != ZigTypeIdMetaType) {
|
|
ErrorMsg *msg = ir_add_error(ira, type_value,
|
|
buf_sprintf("expected error set type, found '%s'", buf_ptr(&type_value->value.type->name)));
|
|
add_error_note(ira->codegen, msg, op_source->source_node,
|
|
buf_sprintf("`||` merges error sets; `or` performs boolean OR"));
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
}
|
|
|
|
ConstExprValue *const_val = ir_resolve_const(ira, type_value, UndefBad);
|
|
if (!const_val)
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
|
|
assert(const_val->data.x_type != nullptr);
|
|
ZigType *result_type = const_val->data.x_type;
|
|
if (result_type->id != ZigTypeIdErrorSet) {
|
|
ErrorMsg *msg = ir_add_error(ira, type_value,
|
|
buf_sprintf("expected error set type, found type '%s'", buf_ptr(&result_type->name)));
|
|
add_error_note(ira->codegen, msg, op_source->source_node,
|
|
buf_sprintf("`||` merges error sets; `or` performs boolean OR"));
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
}
|
|
return result_type;
|
|
}
|
|
|
|
static ZigFn *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) {
|
|
if (fn_value == ira->codegen->invalid_instruction)
|
|
return nullptr;
|
|
|
|
if (type_is_invalid(fn_value->value.type))
|
|
return nullptr;
|
|
|
|
if (fn_value->value.type->id != ZigTypeIdFn) {
|
|
ir_add_error_node(ira, fn_value->source_node,
|
|
buf_sprintf("expected function type, found '%s'", buf_ptr(&fn_value->value.type->name)));
|
|
return nullptr;
|
|
}
|
|
|
|
ConstExprValue *const_val = ir_resolve_const(ira, fn_value, UndefBad);
|
|
if (!const_val)
|
|
return nullptr;
|
|
|
|
assert(const_val->data.x_ptr.special == ConstPtrSpecialFunction);
|
|
return const_val->data.x_ptr.data.fn.fn_entry;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_optional_wrap(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value,
|
|
ZigType *wanted_type, ResultLoc *result_loc)
|
|
{
|
|
assert(wanted_type->id == ZigTypeIdOptional);
|
|
|
|
if (instr_is_comptime(value)) {
|
|
ZigType *payload_type = wanted_type->data.maybe.child_type;
|
|
IrInstruction *casted_payload = ir_implicit_cast(ira, value, payload_type);
|
|
if (type_is_invalid(casted_payload->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ConstExprValue *val = ir_resolve_const(ira, casted_payload, UndefOk);
|
|
if (!val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb,
|
|
source_instr->scope, source_instr->source_node);
|
|
const_instruction->base.value.special = ConstValSpecialStatic;
|
|
if (types_have_same_zig_comptime_repr(wanted_type, payload_type)) {
|
|
copy_const_val(&const_instruction->base.value, val, val->data.x_ptr.mut == ConstPtrMutComptimeConst);
|
|
} else {
|
|
const_instruction->base.value.data.x_optional = val;
|
|
}
|
|
const_instruction->base.value.type = wanted_type;
|
|
return &const_instruction->base;
|
|
}
|
|
|
|
if (result_loc == nullptr && handle_is_ptr(wanted_type)) {
|
|
result_loc = no_result_loc();
|
|
}
|
|
IrInstruction *result_loc_inst = nullptr;
|
|
if (result_loc != nullptr) {
|
|
result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, true, false, true);
|
|
if (type_is_invalid(result_loc_inst->value.type) || instr_is_unreachable(result_loc_inst)) {
|
|
return result_loc_inst;
|
|
}
|
|
}
|
|
IrInstruction *result = ir_build_optional_wrap(ira, source_instr, wanted_type, value, result_loc_inst);
|
|
result->value.data.rh_maybe = RuntimeHintOptionalNonNull;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction *source_instr,
|
|
IrInstruction *value, ZigType *wanted_type, ResultLoc *result_loc)
|
|
{
|
|
assert(wanted_type->id == ZigTypeIdErrorUnion);
|
|
|
|
ZigType *payload_type = wanted_type->data.error_union.payload_type;
|
|
ZigType *err_set_type = wanted_type->data.error_union.err_set_type;
|
|
if (instr_is_comptime(value)) {
|
|
IrInstruction *casted_payload = ir_implicit_cast(ira, value, payload_type);
|
|
if (type_is_invalid(casted_payload->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ConstExprValue *val = ir_resolve_const(ira, casted_payload, UndefBad);
|
|
if (!val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ConstExprValue *err_set_val = create_const_vals(1);
|
|
err_set_val->type = err_set_type;
|
|
err_set_val->special = ConstValSpecialStatic;
|
|
err_set_val->data.x_err_set = nullptr;
|
|
|
|
IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb,
|
|
source_instr->scope, source_instr->source_node);
|
|
const_instruction->base.value.type = wanted_type;
|
|
const_instruction->base.value.special = ConstValSpecialStatic;
|
|
const_instruction->base.value.data.x_err_union.error_set = err_set_val;
|
|
const_instruction->base.value.data.x_err_union.payload = val;
|
|
return &const_instruction->base;
|
|
}
|
|
|
|
IrInstruction *result_loc_inst;
|
|
if (handle_is_ptr(wanted_type)) {
|
|
if (result_loc == nullptr) result_loc = no_result_loc();
|
|
result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, true, false, true);
|
|
if (type_is_invalid(result_loc_inst->value.type) || instr_is_unreachable(result_loc_inst)) {
|
|
return result_loc_inst;
|
|
}
|
|
} else {
|
|
result_loc_inst = nullptr;
|
|
}
|
|
|
|
IrInstruction *result = ir_build_err_wrap_payload(ira, source_instr, wanted_type, value, result_loc_inst);
|
|
result->value.data.rh_error_union = RuntimeHintErrorUnionNonError;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_err_set_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value,
|
|
ZigType *wanted_type)
|
|
{
|
|
assert(value->value.type->id == ZigTypeIdErrorSet);
|
|
assert(wanted_type->id == ZigTypeIdErrorSet);
|
|
|
|
if (instr_is_comptime(value)) {
|
|
ConstExprValue *val = ir_resolve_const(ira, value, UndefBad);
|
|
if (!val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (!resolve_inferred_error_set(ira->codegen, wanted_type, source_instr->source_node)) {
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
if (!type_is_global_error_set(wanted_type)) {
|
|
bool subset = false;
|
|
for (uint32_t i = 0, count = wanted_type->data.error_set.err_count; i < count; i += 1) {
|
|
if (wanted_type->data.error_set.errors[i]->value == val->data.x_err_set->value) {
|
|
subset = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!subset) {
|
|
ir_add_error(ira, source_instr,
|
|
buf_sprintf("error.%s not a member of error set '%s'",
|
|
buf_ptr(&val->data.x_err_set->name), buf_ptr(&wanted_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
|
|
IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb,
|
|
source_instr->scope, source_instr->source_node);
|
|
const_instruction->base.value.type = wanted_type;
|
|
const_instruction->base.value.special = ConstValSpecialStatic;
|
|
const_instruction->base.value.data.x_err_set = val->data.x_err_set;
|
|
return &const_instruction->base;
|
|
}
|
|
|
|
IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type, value, CastOpErrSet);
|
|
result->value.type = wanted_type;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_frame_ptr_to_anyframe(IrAnalyze *ira, IrInstruction *source_instr,
|
|
IrInstruction *value, ZigType *wanted_type)
|
|
{
|
|
if (instr_is_comptime(value)) {
|
|
zig_panic("TODO comptime frame pointer");
|
|
}
|
|
|
|
IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node,
|
|
wanted_type, value, CastOpBitCast);
|
|
result->value.type = wanted_type;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_anyframe_to_anyframe(IrAnalyze *ira, IrInstruction *source_instr,
|
|
IrInstruction *value, ZigType *wanted_type)
|
|
{
|
|
if (instr_is_comptime(value)) {
|
|
zig_panic("TODO comptime anyframe->T to anyframe");
|
|
}
|
|
|
|
IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node,
|
|
wanted_type, value, CastOpBitCast);
|
|
result->value.type = wanted_type;
|
|
return result;
|
|
}
|
|
|
|
|
|
static IrInstruction *ir_analyze_err_wrap_code(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value,
|
|
ZigType *wanted_type, ResultLoc *result_loc)
|
|
{
|
|
assert(wanted_type->id == ZigTypeIdErrorUnion);
|
|
|
|
IrInstruction *casted_value = ir_implicit_cast(ira, value, wanted_type->data.error_union.err_set_type);
|
|
|
|
if (instr_is_comptime(casted_value)) {
|
|
ConstExprValue *val = ir_resolve_const(ira, casted_value, UndefBad);
|
|
if (!val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ConstExprValue *err_set_val = create_const_vals(1);
|
|
err_set_val->special = ConstValSpecialStatic;
|
|
err_set_val->type = wanted_type->data.error_union.err_set_type;
|
|
err_set_val->data.x_err_set = val->data.x_err_set;
|
|
|
|
IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb,
|
|
source_instr->scope, source_instr->source_node);
|
|
const_instruction->base.value.type = wanted_type;
|
|
const_instruction->base.value.special = ConstValSpecialStatic;
|
|
const_instruction->base.value.data.x_err_union.error_set = err_set_val;
|
|
const_instruction->base.value.data.x_err_union.payload = nullptr;
|
|
return &const_instruction->base;
|
|
}
|
|
|
|
IrInstruction *result_loc_inst;
|
|
if (handle_is_ptr(wanted_type)) {
|
|
if (result_loc == nullptr) result_loc = no_result_loc();
|
|
result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, true, false, true);
|
|
if (type_is_invalid(result_loc_inst->value.type) || instr_is_unreachable(result_loc_inst)) {
|
|
return result_loc_inst;
|
|
}
|
|
} else {
|
|
result_loc_inst = nullptr;
|
|
}
|
|
|
|
|
|
IrInstruction *result = ir_build_err_wrap_code(ira, source_instr, wanted_type, value, result_loc_inst);
|
|
result->value.data.rh_error_union = RuntimeHintErrorUnionError;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type) {
|
|
assert(wanted_type->id == ZigTypeIdOptional);
|
|
assert(instr_is_comptime(value));
|
|
|
|
ConstExprValue *val = ir_resolve_const(ira, value, UndefBad);
|
|
assert(val != nullptr);
|
|
|
|
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
|
|
result->value.special = ConstValSpecialStatic;
|
|
if (get_codegen_ptr_type(wanted_type) != nullptr) {
|
|
result->value.data.x_ptr.special = ConstPtrSpecialNull;
|
|
} else if (is_opt_err_set(wanted_type)) {
|
|
result->value.data.x_err_set = nullptr;
|
|
} else {
|
|
result->value.data.x_optional = nullptr;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_null_to_c_pointer(IrAnalyze *ira, IrInstruction *source_instr,
|
|
IrInstruction *value, ZigType *wanted_type)
|
|
{
|
|
assert(wanted_type->id == ZigTypeIdPointer);
|
|
assert(wanted_type->data.pointer.ptr_len == PtrLenC);
|
|
assert(instr_is_comptime(value));
|
|
|
|
ConstExprValue *val = ir_resolve_const(ira, value, UndefBad);
|
|
assert(val != nullptr);
|
|
|
|
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
|
|
result->value.data.x_ptr.special = ConstPtrSpecialNull;
|
|
result->value.data.x_ptr.mut = ConstPtrMutComptimeConst;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *value,
|
|
bool is_const, bool is_volatile)
|
|
{
|
|
Error err;
|
|
|
|
if (type_is_invalid(value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if ((err = type_resolve(ira->codegen, value->value.type, ResolveStatusZeroBitsKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (instr_is_comptime(value)) {
|
|
ConstExprValue *val = ir_resolve_const(ira, value, UndefOk);
|
|
if (!val)
|
|
return ira->codegen->invalid_instruction;
|
|
return ir_get_const_ptr(ira, source_instruction, val, value->value.type,
|
|
ConstPtrMutComptimeConst, is_const, is_volatile, 0);
|
|
}
|
|
|
|
ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, value->value.type,
|
|
is_const, is_volatile, PtrLenSingle, 0, 0, 0, false);
|
|
|
|
IrInstruction *result_loc;
|
|
if (type_has_bits(ptr_type) && !handle_is_ptr(value->value.type)) {
|
|
result_loc = ir_resolve_result(ira, source_instruction, no_result_loc(), value->value.type, nullptr, true,
|
|
false, true);
|
|
} else {
|
|
result_loc = nullptr;
|
|
}
|
|
|
|
IrInstruction *new_instruction = ir_build_ref_gen(ira, source_instruction, ptr_type, value, result_loc);
|
|
new_instruction->value.data.rh_ptr = RuntimeHintPtrStack;
|
|
return new_instruction;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_array_to_slice(IrAnalyze *ira, IrInstruction *source_instr,
|
|
IrInstruction *array_arg, ZigType *wanted_type, ResultLoc *result_loc)
|
|
{
|
|
assert(is_slice(wanted_type));
|
|
// In this function we honor the const-ness of wanted_type, because
|
|
// we may be casting [0]T to []const T which is perfectly valid.
|
|
|
|
IrInstruction *array_ptr = nullptr;
|
|
IrInstruction *array;
|
|
if (array_arg->value.type->id == ZigTypeIdPointer) {
|
|
array = ir_get_deref(ira, source_instr, array_arg, nullptr);
|
|
array_ptr = array_arg;
|
|
} else {
|
|
array = array_arg;
|
|
}
|
|
ZigType *array_type = array->value.type;
|
|
assert(array_type->id == ZigTypeIdArray);
|
|
|
|
if (instr_is_comptime(array) || array_type->data.array.len == 0) {
|
|
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
|
|
init_const_slice(ira->codegen, &result->value, &array->value, 0, array_type->data.array.len, true);
|
|
result->value.type = wanted_type;
|
|
return result;
|
|
}
|
|
|
|
IrInstruction *start = ir_const(ira, source_instr, ira->codegen->builtin_types.entry_usize);
|
|
init_const_usize(ira->codegen, &start->value, 0);
|
|
|
|
IrInstruction *end = ir_const(ira, source_instr, ira->codegen->builtin_types.entry_usize);
|
|
init_const_usize(ira->codegen, &end->value, array_type->data.array.len);
|
|
|
|
if (!array_ptr) array_ptr = ir_get_ref(ira, source_instr, array, true, false);
|
|
|
|
if (result_loc == nullptr) result_loc = no_result_loc();
|
|
IrInstruction *result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr,
|
|
true, false, true);
|
|
if (type_is_invalid(result_loc_inst->value.type) || instr_is_unreachable(result_loc_inst)) {
|
|
return result_loc_inst;
|
|
}
|
|
IrInstruction *result = ir_build_slice_gen(ira, source_instr, wanted_type, array_ptr, start, end, false, result_loc_inst);
|
|
result->value.data.rh_slice.id = RuntimeHintSliceIdLen;
|
|
result->value.data.rh_slice.len = array_type->data.array.len;
|
|
|
|
return result;
|
|
}
|
|
|
|
static ZigType *ir_resolve_union_tag_type(IrAnalyze *ira, IrInstruction *source_instr, ZigType *union_type) {
|
|
assert(union_type->id == ZigTypeIdUnion);
|
|
|
|
Error err;
|
|
if ((err = type_resolve(ira->codegen, union_type, ResolveStatusSizeKnown)))
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
|
|
AstNode *decl_node = union_type->data.unionation.decl_node;
|
|
if (decl_node->data.container_decl.auto_enum || decl_node->data.container_decl.init_arg_expr != nullptr) {
|
|
assert(union_type->data.unionation.tag_type != nullptr);
|
|
return union_type->data.unionation.tag_type;
|
|
} else {
|
|
ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("union '%s' has no tag",
|
|
buf_ptr(&union_type->name)));
|
|
add_error_note(ira->codegen, msg, decl_node, buf_sprintf("consider 'union(enum)' here"));
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
}
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target) {
|
|
Error err;
|
|
|
|
IrInstruction *enum_target;
|
|
ZigType *enum_type;
|
|
if (target->value.type->id == ZigTypeIdUnion) {
|
|
enum_type = ir_resolve_union_tag_type(ira, target, target->value.type);
|
|
if (type_is_invalid(enum_type))
|
|
return ira->codegen->invalid_instruction;
|
|
enum_target = ir_implicit_cast(ira, target, enum_type);
|
|
if (type_is_invalid(enum_target->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
} else if (target->value.type->id == ZigTypeIdEnum) {
|
|
enum_target = target;
|
|
enum_type = target->value.type;
|
|
} else {
|
|
ir_add_error(ira, target,
|
|
buf_sprintf("expected enum, found type '%s'", buf_ptr(&target->value.type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if ((err = type_resolve(ira->codegen, enum_type, ResolveStatusSizeKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *tag_type = enum_type->data.enumeration.tag_int_type;
|
|
assert(tag_type->id == ZigTypeIdInt || tag_type->id == ZigTypeIdComptimeInt);
|
|
|
|
// If there is only one possible tag, then we know at comptime what it is.
|
|
if (enum_type->data.enumeration.layout == ContainerLayoutAuto &&
|
|
enum_type->data.enumeration.src_field_count == 1)
|
|
{
|
|
assert(tag_type == ira->codegen->builtin_types.entry_num_lit_int);
|
|
IrInstruction *result = ir_const(ira, source_instr, tag_type);
|
|
init_const_bigint(&result->value, tag_type,
|
|
&enum_type->data.enumeration.fields[0].value);
|
|
return result;
|
|
}
|
|
|
|
if (instr_is_comptime(enum_target)) {
|
|
ConstExprValue *val = ir_resolve_const(ira, enum_target, UndefBad);
|
|
if (!val)
|
|
return ira->codegen->invalid_instruction;
|
|
IrInstruction *result = ir_const(ira, source_instr, tag_type);
|
|
init_const_bigint(&result->value, tag_type, &val->data.x_enum_tag);
|
|
return result;
|
|
}
|
|
|
|
IrInstruction *result = ir_build_widen_or_shorten(&ira->new_irb, source_instr->scope,
|
|
source_instr->source_node, enum_target);
|
|
result->value.type = tag_type;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_union_to_tag(IrAnalyze *ira, IrInstruction *source_instr,
|
|
IrInstruction *target, ZigType *wanted_type)
|
|
{
|
|
assert(target->value.type->id == ZigTypeIdUnion);
|
|
assert(wanted_type->id == ZigTypeIdEnum);
|
|
assert(wanted_type == target->value.type->data.unionation.tag_type);
|
|
|
|
if (instr_is_comptime(target)) {
|
|
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
|
|
if (!val)
|
|
return ira->codegen->invalid_instruction;
|
|
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
|
|
result->value.special = ConstValSpecialStatic;
|
|
result->value.type = wanted_type;
|
|
bigint_init_bigint(&result->value.data.x_enum_tag, &val->data.x_union.tag);
|
|
return result;
|
|
}
|
|
|
|
// If there is only 1 possible tag, then we know at comptime what it is.
|
|
if (wanted_type->data.enumeration.layout == ContainerLayoutAuto &&
|
|
wanted_type->data.enumeration.src_field_count == 1)
|
|
{
|
|
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
|
|
result->value.special = ConstValSpecialStatic;
|
|
result->value.type = wanted_type;
|
|
TypeEnumField *enum_field = target->value.type->data.unionation.fields[0].enum_field;
|
|
bigint_init_bigint(&result->value.data.x_enum_tag, &enum_field->value);
|
|
return result;
|
|
}
|
|
|
|
IrInstruction *result = ir_build_union_tag(&ira->new_irb, source_instr->scope,
|
|
source_instr->source_node, target);
|
|
result->value.type = wanted_type;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_undefined_to_anything(IrAnalyze *ira, IrInstruction *source_instr,
|
|
IrInstruction *target, ZigType *wanted_type)
|
|
{
|
|
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
|
|
init_const_undefined(ira->codegen, &result->value);
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_enum_to_union(IrAnalyze *ira, IrInstruction *source_instr,
|
|
IrInstruction *uncasted_target, ZigType *wanted_type)
|
|
{
|
|
Error err;
|
|
assert(wanted_type->id == ZigTypeIdUnion);
|
|
|
|
if ((err = type_resolve(ira->codegen, wanted_type, ResolveStatusZeroBitsKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *target = ir_implicit_cast(ira, uncasted_target, wanted_type->data.unionation.tag_type);
|
|
if (type_is_invalid(target->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (instr_is_comptime(target)) {
|
|
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
|
|
if (!val)
|
|
return ira->codegen->invalid_instruction;
|
|
TypeUnionField *union_field = find_union_field_by_tag(wanted_type, &val->data.x_enum_tag);
|
|
assert(union_field != nullptr);
|
|
if ((err = type_resolve(ira->codegen, union_field->type_entry, ResolveStatusZeroBitsKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
switch (type_has_one_possible_value(ira->codegen, union_field->type_entry)) {
|
|
case OnePossibleValueInvalid:
|
|
return ira->codegen->invalid_instruction;
|
|
case OnePossibleValueNo: {
|
|
AstNode *field_node = wanted_type->data.unionation.decl_node->data.container_decl.fields.at(
|
|
union_field->enum_field->decl_index);
|
|
ErrorMsg *msg = ir_add_error(ira, source_instr,
|
|
buf_sprintf("cast to union '%s' must initialize '%s' field '%s'",
|
|
buf_ptr(&wanted_type->name),
|
|
buf_ptr(&union_field->type_entry->name),
|
|
buf_ptr(union_field->name)));
|
|
add_error_note(ira->codegen, msg, field_node,
|
|
buf_sprintf("field '%s' declared here", buf_ptr(union_field->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
case OnePossibleValueYes:
|
|
break;
|
|
}
|
|
|
|
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
|
|
result->value.special = ConstValSpecialStatic;
|
|
result->value.type = wanted_type;
|
|
bigint_init_bigint(&result->value.data.x_union.tag, &val->data.x_enum_tag);
|
|
result->value.data.x_union.payload = create_const_vals(1);
|
|
result->value.data.x_union.payload->special = ConstValSpecialStatic;
|
|
result->value.data.x_union.payload->type = union_field->type_entry;
|
|
return result;
|
|
}
|
|
|
|
// if the union has all fields 0 bits, we can do it
|
|
// and in fact it's a noop cast because the union value is just the enum value
|
|
if (wanted_type->data.unionation.gen_field_count == 0) {
|
|
IrInstruction *result = ir_build_cast(&ira->new_irb, target->scope, target->source_node, wanted_type, target, CastOpNoop);
|
|
result->value.type = wanted_type;
|
|
return result;
|
|
}
|
|
|
|
ErrorMsg *msg = ir_add_error(ira, source_instr,
|
|
buf_sprintf("runtime cast to union '%s' which has non-void fields",
|
|
buf_ptr(&wanted_type->name)));
|
|
for (uint32_t i = 0; i < wanted_type->data.unionation.src_field_count; i += 1) {
|
|
TypeUnionField *union_field = &wanted_type->data.unionation.fields[i];
|
|
if (type_has_bits(union_field->type_entry)) {
|
|
AstNode *field_node = wanted_type->data.unionation.decl_node->data.container_decl.fields.at(i);
|
|
add_error_note(ira->codegen, msg, field_node,
|
|
buf_sprintf("field '%s' has type '%s'",
|
|
buf_ptr(union_field->name),
|
|
buf_ptr(&union_field->type_entry->name)));
|
|
}
|
|
}
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction *source_instr,
|
|
IrInstruction *target, ZigType *wanted_type)
|
|
{
|
|
assert(wanted_type->id == ZigTypeIdInt || wanted_type->id == ZigTypeIdFloat);
|
|
|
|
if (instr_is_comptime(target)) {
|
|
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
|
|
if (!val)
|
|
return ira->codegen->invalid_instruction;
|
|
if (wanted_type->id == ZigTypeIdInt) {
|
|
if (bigint_cmp_zero(&val->data.x_bigint) == CmpLT && !wanted_type->data.integral.is_signed) {
|
|
ir_add_error(ira, source_instr,
|
|
buf_sprintf("attempt to cast negative value to unsigned integer"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
if (!bigint_fits_in_bits(&val->data.x_bigint, wanted_type->data.integral.bit_count,
|
|
wanted_type->data.integral.is_signed))
|
|
{
|
|
ir_add_error(ira, source_instr,
|
|
buf_sprintf("cast from '%s' to '%s' truncates bits",
|
|
buf_ptr(&target->value.type->name), buf_ptr(&wanted_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
|
|
result->value.type = wanted_type;
|
|
if (wanted_type->id == ZigTypeIdInt) {
|
|
bigint_init_bigint(&result->value.data.x_bigint, &val->data.x_bigint);
|
|
} else {
|
|
float_init_float(&result->value, val);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// If the destination integer type has no bits, then we can emit a comptime
|
|
// zero. However, we still want to emit a runtime safety check to make sure
|
|
// the target is zero.
|
|
if (!type_has_bits(wanted_type)) {
|
|
assert(wanted_type->id == ZigTypeIdInt);
|
|
assert(type_has_bits(target->value.type));
|
|
ir_build_assert_zero(ira, source_instr, target);
|
|
IrInstruction *result = ir_const_unsigned(ira, source_instr, 0);
|
|
result->value.type = wanted_type;
|
|
return result;
|
|
}
|
|
|
|
IrInstruction *result = ir_build_widen_or_shorten(&ira->new_irb, source_instr->scope,
|
|
source_instr->source_node, target);
|
|
result->value.type = wanted_type;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *source_instr,
|
|
IrInstruction *target, ZigType *wanted_type)
|
|
{
|
|
Error err;
|
|
assert(wanted_type->id == ZigTypeIdEnum);
|
|
|
|
ZigType *actual_type = target->value.type;
|
|
|
|
if ((err = ensure_complete_type(ira->codegen, wanted_type)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (actual_type != wanted_type->data.enumeration.tag_int_type) {
|
|
ir_add_error(ira, source_instr,
|
|
buf_sprintf("integer to enum cast from '%s' instead of its tag type, '%s'",
|
|
buf_ptr(&actual_type->name),
|
|
buf_ptr(&wanted_type->data.enumeration.tag_int_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
assert(actual_type->id == ZigTypeIdInt || actual_type->id == ZigTypeIdComptimeInt);
|
|
|
|
if (instr_is_comptime(target)) {
|
|
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
|
|
if (!val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
TypeEnumField *field = find_enum_field_by_tag(wanted_type, &val->data.x_bigint);
|
|
if (field == nullptr) {
|
|
Buf *val_buf = buf_alloc();
|
|
bigint_append_buf(val_buf, &val->data.x_bigint, 10);
|
|
ErrorMsg *msg = ir_add_error(ira, source_instr,
|
|
buf_sprintf("enum '%s' has no tag matching integer value %s",
|
|
buf_ptr(&wanted_type->name), buf_ptr(val_buf)));
|
|
add_error_note(ira->codegen, msg, wanted_type->data.enumeration.decl_node,
|
|
buf_sprintf("'%s' declared here", buf_ptr(&wanted_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
|
|
bigint_init_bigint(&result->value.data.x_enum_tag, &val->data.x_bigint);
|
|
return result;
|
|
}
|
|
|
|
IrInstruction *result = ir_build_int_to_enum(&ira->new_irb, source_instr->scope,
|
|
source_instr->source_node, nullptr, target);
|
|
result->value.type = wanted_type;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_number_to_literal(IrAnalyze *ira, IrInstruction *source_instr,
|
|
IrInstruction *target, ZigType *wanted_type)
|
|
{
|
|
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
|
|
if (!val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
|
|
if (wanted_type->id == ZigTypeIdComptimeFloat) {
|
|
float_init_float(&result->value, val);
|
|
} else if (wanted_type->id == ZigTypeIdComptimeInt) {
|
|
bigint_init_bigint(&result->value.data.x_bigint, &val->data.x_bigint);
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_int_to_err(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target,
|
|
ZigType *wanted_type)
|
|
{
|
|
assert(target->value.type->id == ZigTypeIdInt);
|
|
assert(!target->value.type->data.integral.is_signed);
|
|
assert(wanted_type->id == ZigTypeIdErrorSet);
|
|
|
|
if (instr_is_comptime(target)) {
|
|
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
|
|
if (!val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
|
|
|
|
if (!resolve_inferred_error_set(ira->codegen, wanted_type, source_instr->source_node)) {
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (type_is_global_error_set(wanted_type)) {
|
|
BigInt err_count;
|
|
bigint_init_unsigned(&err_count, ira->codegen->errors_by_index.length);
|
|
|
|
if (bigint_cmp_zero(&val->data.x_bigint) == CmpEQ || bigint_cmp(&val->data.x_bigint, &err_count) != CmpLT) {
|
|
Buf *val_buf = buf_alloc();
|
|
bigint_append_buf(val_buf, &val->data.x_bigint, 10);
|
|
ir_add_error(ira, source_instr,
|
|
buf_sprintf("integer value %s represents no error", buf_ptr(val_buf)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
size_t index = bigint_as_unsigned(&val->data.x_bigint);
|
|
result->value.data.x_err_set = ira->codegen->errors_by_index.at(index);
|
|
return result;
|
|
} else {
|
|
ErrorTableEntry *err = nullptr;
|
|
BigInt err_int;
|
|
|
|
for (uint32_t i = 0, count = wanted_type->data.error_set.err_count; i < count; i += 1) {
|
|
ErrorTableEntry *this_err = wanted_type->data.error_set.errors[i];
|
|
bigint_init_unsigned(&err_int, this_err->value);
|
|
if (bigint_cmp(&val->data.x_bigint, &err_int) == CmpEQ) {
|
|
err = this_err;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (err == nullptr) {
|
|
Buf *val_buf = buf_alloc();
|
|
bigint_append_buf(val_buf, &val->data.x_bigint, 10);
|
|
ir_add_error(ira, source_instr,
|
|
buf_sprintf("integer value %s represents no error in '%s'", buf_ptr(val_buf), buf_ptr(&wanted_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
result->value.data.x_err_set = err;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
IrInstruction *result = ir_build_int_to_err(&ira->new_irb, source_instr->scope, source_instr->source_node, target);
|
|
result->value.type = wanted_type;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target,
|
|
ZigType *wanted_type)
|
|
{
|
|
assert(wanted_type->id == ZigTypeIdInt);
|
|
|
|
ZigType *err_type = target->value.type;
|
|
|
|
if (instr_is_comptime(target)) {
|
|
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
|
|
if (!val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
|
|
|
|
ErrorTableEntry *err;
|
|
if (err_type->id == ZigTypeIdErrorUnion) {
|
|
err = val->data.x_err_union.error_set->data.x_err_set;
|
|
} else if (err_type->id == ZigTypeIdErrorSet) {
|
|
err = val->data.x_err_set;
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
result->value.type = wanted_type;
|
|
uint64_t err_value = err ? err->value : 0;
|
|
bigint_init_unsigned(&result->value.data.x_bigint, err_value);
|
|
|
|
if (!bigint_fits_in_bits(&result->value.data.x_bigint,
|
|
wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed))
|
|
{
|
|
ir_add_error_node(ira, source_instr->source_node,
|
|
buf_sprintf("error code '%s' does not fit in '%s'",
|
|
buf_ptr(&err->name), buf_ptr(&wanted_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
ZigType *err_set_type;
|
|
if (err_type->id == ZigTypeIdErrorUnion) {
|
|
err_set_type = err_type->data.error_union.err_set_type;
|
|
} else if (err_type->id == ZigTypeIdErrorSet) {
|
|
err_set_type = err_type;
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
if (!type_is_global_error_set(err_set_type)) {
|
|
if (!resolve_inferred_error_set(ira->codegen, err_set_type, source_instr->source_node)) {
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
if (err_set_type->data.error_set.err_count == 0) {
|
|
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
|
|
bigint_init_unsigned(&result->value.data.x_bigint, 0);
|
|
return result;
|
|
} else if (err_set_type->data.error_set.err_count == 1) {
|
|
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
|
|
ErrorTableEntry *err = err_set_type->data.error_set.errors[0];
|
|
bigint_init_unsigned(&result->value.data.x_bigint, err->value);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
BigInt bn;
|
|
bigint_init_unsigned(&bn, ira->codegen->errors_by_index.length);
|
|
if (!bigint_fits_in_bits(&bn, wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed)) {
|
|
ir_add_error_node(ira, source_instr->source_node,
|
|
buf_sprintf("too many error values to fit in '%s'", buf_ptr(&wanted_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *result = ir_build_err_to_int(&ira->new_irb, source_instr->scope, source_instr->source_node, target);
|
|
result->value.type = wanted_type;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_ptr_to_array(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target,
|
|
ZigType *wanted_type)
|
|
{
|
|
assert(wanted_type->id == ZigTypeIdPointer);
|
|
Error err;
|
|
if ((err = type_resolve(ira->codegen, target->value.type->data.pointer.child_type, ResolveStatusAlignmentKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
assert((wanted_type->data.pointer.is_const && target->value.type->data.pointer.is_const) || !target->value.type->data.pointer.is_const);
|
|
wanted_type = adjust_ptr_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, target->value.type));
|
|
ZigType *array_type = wanted_type->data.pointer.child_type;
|
|
assert(array_type->id == ZigTypeIdArray);
|
|
assert(array_type->data.array.len == 1);
|
|
|
|
if (instr_is_comptime(target)) {
|
|
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
|
|
if (!val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
assert(val->type->id == ZigTypeIdPointer);
|
|
ConstExprValue *pointee = const_ptr_pointee(ira, ira->codegen, val, source_instr->source_node);
|
|
if (pointee == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
if (pointee->special != ConstValSpecialRuntime) {
|
|
ConstExprValue *array_val = create_const_vals(1);
|
|
array_val->special = ConstValSpecialStatic;
|
|
array_val->type = array_type;
|
|
array_val->data.x_array.special = ConstArraySpecialNone;
|
|
array_val->data.x_array.data.s_none.elements = pointee;
|
|
array_val->parent.id = ConstParentIdScalar;
|
|
array_val->parent.data.p_scalar.scalar_val = pointee;
|
|
|
|
IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb,
|
|
source_instr->scope, source_instr->source_node);
|
|
const_instruction->base.value.type = wanted_type;
|
|
const_instruction->base.value.special = ConstValSpecialStatic;
|
|
const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialRef;
|
|
const_instruction->base.value.data.x_ptr.data.ref.pointee = array_val;
|
|
const_instruction->base.value.data.x_ptr.mut = val->data.x_ptr.mut;
|
|
return &const_instruction->base;
|
|
}
|
|
}
|
|
|
|
// pointer to array and pointer to single item are represented the same way at runtime
|
|
IrInstruction *result = ir_build_cast(&ira->new_irb, target->scope, target->source_node,
|
|
wanted_type, target, CastOpBitCast);
|
|
result->value.type = wanted_type;
|
|
return result;
|
|
}
|
|
|
|
static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCastOnly *cast_result,
|
|
ErrorMsg *parent_msg)
|
|
{
|
|
switch (cast_result->id) {
|
|
case ConstCastResultIdOk:
|
|
zig_unreachable();
|
|
case ConstCastResultIdInvalid:
|
|
zig_unreachable();
|
|
case ConstCastResultIdOptionalChild: {
|
|
ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node,
|
|
buf_sprintf("optional type child '%s' cannot cast into optional type child '%s'",
|
|
buf_ptr(&cast_result->data.optional->actual_child->name),
|
|
buf_ptr(&cast_result->data.optional->wanted_child->name)));
|
|
report_recursive_error(ira, source_node, &cast_result->data.optional->child, msg);
|
|
break;
|
|
}
|
|
case ConstCastResultIdErrorUnionErrorSet: {
|
|
ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node,
|
|
buf_sprintf("error set '%s' cannot cast into error set '%s'",
|
|
buf_ptr(&cast_result->data.error_union_error_set->actual_err_set->name),
|
|
buf_ptr(&cast_result->data.error_union_error_set->wanted_err_set->name)));
|
|
report_recursive_error(ira, source_node, &cast_result->data.error_union_error_set->child, msg);
|
|
break;
|
|
}
|
|
case ConstCastResultIdErrSet: {
|
|
ZigList<ErrorTableEntry *> *missing_errors = &cast_result->data.error_set_mismatch->missing_errors;
|
|
for (size_t i = 0; i < missing_errors->length; i += 1) {
|
|
ErrorTableEntry *error_entry = missing_errors->at(i);
|
|
add_error_note(ira->codegen, parent_msg, error_entry->decl_node,
|
|
buf_sprintf("'error.%s' not a member of destination error set", buf_ptr(&error_entry->name)));
|
|
}
|
|
break;
|
|
}
|
|
case ConstCastResultIdErrSetGlobal: {
|
|
add_error_note(ira->codegen, parent_msg, source_node,
|
|
buf_sprintf("cannot cast global error set into smaller set"));
|
|
break;
|
|
}
|
|
case ConstCastResultIdPointerChild: {
|
|
ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node,
|
|
buf_sprintf("pointer type child '%s' cannot cast into pointer type child '%s'",
|
|
buf_ptr(&cast_result->data.pointer_mismatch->actual_child->name),
|
|
buf_ptr(&cast_result->data.pointer_mismatch->wanted_child->name)));
|
|
report_recursive_error(ira, source_node, &cast_result->data.pointer_mismatch->child, msg);
|
|
break;
|
|
}
|
|
case ConstCastResultIdSliceChild: {
|
|
ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node,
|
|
buf_sprintf("slice type child '%s' cannot cast into slice type child '%s'",
|
|
buf_ptr(&cast_result->data.slice_mismatch->actual_child->name),
|
|
buf_ptr(&cast_result->data.slice_mismatch->wanted_child->name)));
|
|
report_recursive_error(ira, source_node, &cast_result->data.slice_mismatch->child, msg);
|
|
break;
|
|
}
|
|
case ConstCastResultIdErrorUnionPayload: {
|
|
ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node,
|
|
buf_sprintf("error union payload '%s' cannot cast into error union payload '%s'",
|
|
buf_ptr(&cast_result->data.error_union_payload->actual_payload->name),
|
|
buf_ptr(&cast_result->data.error_union_payload->wanted_payload->name)));
|
|
report_recursive_error(ira, source_node, &cast_result->data.error_union_payload->child, msg);
|
|
break;
|
|
}
|
|
case ConstCastResultIdType: {
|
|
AstNode *wanted_decl_node = type_decl_node(cast_result->data.type_mismatch->wanted_type);
|
|
AstNode *actual_decl_node = type_decl_node(cast_result->data.type_mismatch->actual_type);
|
|
if (wanted_decl_node != nullptr) {
|
|
add_error_note(ira->codegen, parent_msg, wanted_decl_node,
|
|
buf_sprintf("%s declared here",
|
|
buf_ptr(&cast_result->data.type_mismatch->wanted_type->name)));
|
|
}
|
|
if (actual_decl_node != nullptr) {
|
|
add_error_note(ira->codegen, parent_msg, actual_decl_node,
|
|
buf_sprintf("%s declared here",
|
|
buf_ptr(&cast_result->data.type_mismatch->actual_type->name)));
|
|
}
|
|
break;
|
|
}
|
|
case ConstCastResultIdFnArg: {
|
|
ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node,
|
|
buf_sprintf("parameter %" ZIG_PRI_usize ": '%s' cannot cast into '%s'",
|
|
cast_result->data.fn_arg.arg_index,
|
|
buf_ptr(&cast_result->data.fn_arg.actual_param_type->name),
|
|
buf_ptr(&cast_result->data.fn_arg.expected_param_type->name)));
|
|
report_recursive_error(ira, source_node, cast_result->data.fn_arg.child, msg);
|
|
break;
|
|
}
|
|
case ConstCastResultIdBadAllowsZero: {
|
|
ZigType *wanted_type = cast_result->data.bad_allows_zero->wanted_type;
|
|
ZigType *actual_type = cast_result->data.bad_allows_zero->actual_type;
|
|
bool wanted_allows_zero = ptr_allows_addr_zero(wanted_type);
|
|
bool actual_allows_zero = ptr_allows_addr_zero(actual_type);
|
|
if (actual_allows_zero && !wanted_allows_zero) {
|
|
add_error_note(ira->codegen, parent_msg, source_node,
|
|
buf_sprintf("'%s' could have null values which are illegal in type '%s'",
|
|
buf_ptr(&actual_type->name),
|
|
buf_ptr(&wanted_type->name)));
|
|
} else {
|
|
add_error_note(ira->codegen, parent_msg, source_node,
|
|
buf_sprintf("mutable '%s' allows illegal null values stored to type '%s'",
|
|
buf_ptr(&wanted_type->name),
|
|
buf_ptr(&actual_type->name)));
|
|
}
|
|
break;
|
|
}
|
|
case ConstCastResultIdFnIsGeneric:
|
|
add_error_note(ira->codegen, parent_msg, source_node,
|
|
buf_sprintf("only one of the functions is generic"));
|
|
break;
|
|
case ConstCastResultIdFnAlign: // TODO
|
|
case ConstCastResultIdFnCC: // TODO
|
|
case ConstCastResultIdFnVarArgs: // TODO
|
|
case ConstCastResultIdFnReturnType: // TODO
|
|
case ConstCastResultIdFnArgCount: // TODO
|
|
case ConstCastResultIdFnGenericArgCount: // TODO
|
|
case ConstCastResultIdFnArgNoAlias: // TODO
|
|
case ConstCastResultIdUnresolvedInferredErrSet: // TODO
|
|
case ConstCastResultIdAsyncAllocatorType: // TODO
|
|
break;
|
|
}
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_array_to_vector(IrAnalyze *ira, IrInstruction *source_instr,
|
|
IrInstruction *array, ZigType *vector_type)
|
|
{
|
|
if (instr_is_comptime(array)) {
|
|
// arrays and vectors have the same ConstExprValue representation
|
|
IrInstruction *result = ir_const(ira, source_instr, vector_type);
|
|
copy_const_val(&result->value, &array->value, false);
|
|
result->value.type = vector_type;
|
|
return result;
|
|
}
|
|
return ir_build_array_to_vector(ira, source_instr, array, vector_type);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_vector_to_array(IrAnalyze *ira, IrInstruction *source_instr,
|
|
IrInstruction *vector, ZigType *array_type, ResultLoc *result_loc)
|
|
{
|
|
if (instr_is_comptime(vector)) {
|
|
// arrays and vectors have the same ConstExprValue representation
|
|
IrInstruction *result = ir_const(ira, source_instr, array_type);
|
|
copy_const_val(&result->value, &vector->value, false);
|
|
result->value.type = array_type;
|
|
return result;
|
|
}
|
|
if (result_loc == nullptr) {
|
|
result_loc = no_result_loc();
|
|
}
|
|
IrInstruction *result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, array_type, nullptr,
|
|
true, false, true);
|
|
if (type_is_invalid(result_loc_inst->value.type) || instr_is_unreachable(result_loc_inst)) {
|
|
return result_loc_inst;
|
|
}
|
|
return ir_build_vector_to_array(ira, source_instr, array_type, vector, result_loc_inst);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_int_to_c_ptr(IrAnalyze *ira, IrInstruction *source_instr,
|
|
IrInstruction *integer, ZigType *dest_type)
|
|
{
|
|
IrInstruction *unsigned_integer;
|
|
if (instr_is_comptime(integer)) {
|
|
unsigned_integer = integer;
|
|
} else {
|
|
assert(integer->value.type->id == ZigTypeIdInt);
|
|
|
|
if (integer->value.type->data.integral.bit_count >
|
|
ira->codegen->builtin_types.entry_usize->data.integral.bit_count)
|
|
{
|
|
ir_add_error(ira, source_instr,
|
|
buf_sprintf("integer type '%s' too big for implicit @intToPtr to type '%s'",
|
|
buf_ptr(&integer->value.type->name),
|
|
buf_ptr(&dest_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (integer->value.type->data.integral.is_signed) {
|
|
ZigType *unsigned_int_type = get_int_type(ira->codegen, false,
|
|
integer->value.type->data.integral.bit_count);
|
|
unsigned_integer = ir_analyze_bit_cast(ira, source_instr, integer, unsigned_int_type);
|
|
if (type_is_invalid(unsigned_integer->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
} else {
|
|
unsigned_integer = integer;
|
|
}
|
|
}
|
|
|
|
return ir_analyze_int_to_ptr(ira, source_instr, unsigned_integer, dest_type);
|
|
}
|
|
|
|
static bool is_pointery_and_elem_is_not_pointery(ZigType *ty) {
|
|
if (ty->id == ZigTypeIdPointer) return ty->data.pointer.child_type->id != ZigTypeIdPointer;
|
|
if (ty->id == ZigTypeIdFn) return true;
|
|
if (ty->id == ZigTypeIdOptional) {
|
|
ZigType *ptr_ty = ty->data.maybe.child_type;
|
|
if (ptr_ty->id == ZigTypeIdPointer) return ptr_ty->data.pointer.child_type->id != ZigTypeIdPointer;
|
|
if (ptr_ty->id == ZigTypeIdFn) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr,
|
|
ZigType *wanted_type, IrInstruction *value, ResultLoc *result_loc)
|
|
{
|
|
Error err;
|
|
ZigType *actual_type = value->value.type;
|
|
AstNode *source_node = source_instr->source_node;
|
|
|
|
if (type_is_invalid(wanted_type) || type_is_invalid(actual_type)) {
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
// perfect match or non-const to const
|
|
ConstCastOnly const_cast_result = types_match_const_cast_only(ira, wanted_type, actual_type,
|
|
source_node, false);
|
|
if (const_cast_result.id == ConstCastResultIdInvalid)
|
|
return ira->codegen->invalid_instruction;
|
|
if (const_cast_result.id == ConstCastResultIdOk) {
|
|
return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop);
|
|
}
|
|
|
|
// cast from T to ?T
|
|
// note that the *T to ?*T case is handled via the "ConstCastOnly" mechanism
|
|
if (wanted_type->id == ZigTypeIdOptional) {
|
|
ZigType *wanted_child_type = wanted_type->data.maybe.child_type;
|
|
if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node,
|
|
false).id == ConstCastResultIdOk)
|
|
{
|
|
return ir_analyze_optional_wrap(ira, source_instr, value, wanted_type, result_loc);
|
|
} else if (actual_type->id == ZigTypeIdComptimeInt ||
|
|
actual_type->id == ZigTypeIdComptimeFloat)
|
|
{
|
|
if (ir_num_lit_fits_in_other_type(ira, value, wanted_child_type, true)) {
|
|
return ir_analyze_optional_wrap(ira, source_instr, value, wanted_type, result_loc);
|
|
} else {
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
} else if (
|
|
wanted_child_type->id == ZigTypeIdPointer &&
|
|
wanted_child_type->data.pointer.ptr_len == PtrLenUnknown &&
|
|
actual_type->id == ZigTypeIdPointer &&
|
|
actual_type->data.pointer.ptr_len == PtrLenSingle &&
|
|
actual_type->data.pointer.child_type->id == ZigTypeIdArray)
|
|
{
|
|
if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type, ResolveStatusAlignmentKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
if ((err = type_resolve(ira->codegen, wanted_child_type->data.pointer.child_type, ResolveStatusAlignmentKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
if (get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, wanted_child_type) &&
|
|
types_match_const_cast_only(ira, wanted_child_type->data.pointer.child_type,
|
|
actual_type->data.pointer.child_type->data.array.child_type, source_node,
|
|
!wanted_child_type->data.pointer.is_const).id == ConstCastResultIdOk)
|
|
{
|
|
IrInstruction *cast1 = ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value,
|
|
wanted_child_type);
|
|
if (type_is_invalid(cast1->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
return ir_analyze_optional_wrap(ira, source_instr, cast1, wanted_type, result_loc);
|
|
}
|
|
}
|
|
}
|
|
|
|
// T to E!T
|
|
if (wanted_type->id == ZigTypeIdErrorUnion) {
|
|
if (types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type, actual_type,
|
|
source_node, false).id == ConstCastResultIdOk)
|
|
{
|
|
return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type, result_loc);
|
|
} else if (actual_type->id == ZigTypeIdComptimeInt ||
|
|
actual_type->id == ZigTypeIdComptimeFloat)
|
|
{
|
|
if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.error_union.payload_type, true)) {
|
|
return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type, result_loc);
|
|
} else {
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
}
|
|
|
|
// cast from T to E!?T
|
|
if (wanted_type->id == ZigTypeIdErrorUnion &&
|
|
wanted_type->data.error_union.payload_type->id == ZigTypeIdOptional &&
|
|
actual_type->id != ZigTypeIdOptional)
|
|
{
|
|
ZigType *wanted_child_type = wanted_type->data.error_union.payload_type->data.maybe.child_type;
|
|
if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node, false).id == ConstCastResultIdOk ||
|
|
actual_type->id == ZigTypeIdNull ||
|
|
actual_type->id == ZigTypeIdComptimeInt ||
|
|
actual_type->id == ZigTypeIdComptimeFloat)
|
|
{
|
|
IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value, nullptr);
|
|
if (type_is_invalid(cast1->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1, result_loc);
|
|
if (type_is_invalid(cast2->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
return cast2;
|
|
}
|
|
}
|
|
|
|
|
|
// cast from comptime-known number to another number type
|
|
if (instr_is_comptime(value) &&
|
|
(actual_type->id == ZigTypeIdInt || actual_type->id == ZigTypeIdComptimeInt ||
|
|
actual_type->id == ZigTypeIdFloat || actual_type->id == ZigTypeIdComptimeFloat) &&
|
|
(wanted_type->id == ZigTypeIdInt || wanted_type->id == ZigTypeIdComptimeInt ||
|
|
wanted_type->id == ZigTypeIdFloat || wanted_type->id == ZigTypeIdComptimeFloat))
|
|
{
|
|
if (value->value.special == ConstValSpecialUndef) {
|
|
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
|
|
result->value.special = ConstValSpecialUndef;
|
|
return result;
|
|
}
|
|
if (ir_num_lit_fits_in_other_type(ira, value, wanted_type, true)) {
|
|
if (wanted_type->id == ZigTypeIdComptimeInt || wanted_type->id == ZigTypeIdInt) {
|
|
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
|
|
if (actual_type->id == ZigTypeIdComptimeInt || actual_type->id == ZigTypeIdInt) {
|
|
bigint_init_bigint(&result->value.data.x_bigint, &value->value.data.x_bigint);
|
|
} else {
|
|
float_init_bigint(&result->value.data.x_bigint, &value->value);
|
|
}
|
|
return result;
|
|
} else if (wanted_type->id == ZigTypeIdComptimeFloat || wanted_type->id == ZigTypeIdFloat) {
|
|
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
|
|
if (actual_type->id == ZigTypeIdComptimeInt || actual_type->id == ZigTypeIdInt) {
|
|
BigFloat bf;
|
|
bigfloat_init_bigint(&bf, &value->value.data.x_bigint);
|
|
float_init_bigfloat(&result->value, &bf);
|
|
} else {
|
|
float_init_float(&result->value, &value->value);
|
|
}
|
|
return result;
|
|
}
|
|
zig_unreachable();
|
|
} else {
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
|
|
// widening conversion
|
|
if (wanted_type->id == ZigTypeIdInt &&
|
|
actual_type->id == ZigTypeIdInt &&
|
|
wanted_type->data.integral.is_signed == actual_type->data.integral.is_signed &&
|
|
wanted_type->data.integral.bit_count >= actual_type->data.integral.bit_count)
|
|
{
|
|
return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type);
|
|
}
|
|
|
|
// small enough unsigned ints can get casted to large enough signed ints
|
|
if (wanted_type->id == ZigTypeIdInt && wanted_type->data.integral.is_signed &&
|
|
actual_type->id == ZigTypeIdInt && !actual_type->data.integral.is_signed &&
|
|
wanted_type->data.integral.bit_count > actual_type->data.integral.bit_count)
|
|
{
|
|
return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type);
|
|
}
|
|
|
|
// float widening conversion
|
|
if (wanted_type->id == ZigTypeIdFloat &&
|
|
actual_type->id == ZigTypeIdFloat &&
|
|
wanted_type->data.floating.bit_count >= actual_type->data.floating.bit_count)
|
|
{
|
|
return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type);
|
|
}
|
|
|
|
|
|
// cast from [N]T to []const T
|
|
// TODO: once https://github.com/ziglang/zig/issues/265 lands, remove this
|
|
if (is_slice(wanted_type) && actual_type->id == ZigTypeIdArray) {
|
|
ZigType *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
|
|
assert(ptr_type->id == ZigTypeIdPointer);
|
|
if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
|
|
types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type,
|
|
source_node, false).id == ConstCastResultIdOk)
|
|
{
|
|
return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type, result_loc);
|
|
}
|
|
}
|
|
|
|
// cast from [N]T to ?[]const T
|
|
// TODO: once https://github.com/ziglang/zig/issues/265 lands, remove this
|
|
if (wanted_type->id == ZigTypeIdOptional &&
|
|
is_slice(wanted_type->data.maybe.child_type) &&
|
|
actual_type->id == ZigTypeIdArray)
|
|
{
|
|
ZigType *ptr_type =
|
|
wanted_type->data.maybe.child_type->data.structure.fields[slice_ptr_index].type_entry;
|
|
assert(ptr_type->id == ZigTypeIdPointer);
|
|
if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
|
|
types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type,
|
|
source_node, false).id == ConstCastResultIdOk)
|
|
{
|
|
IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.maybe.child_type, value, nullptr);
|
|
if (type_is_invalid(cast1->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1, result_loc);
|
|
if (type_is_invalid(cast2->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
return cast2;
|
|
}
|
|
}
|
|
|
|
// *[N]T to [*]T and [*c]T
|
|
if (wanted_type->id == ZigTypeIdPointer &&
|
|
(wanted_type->data.pointer.ptr_len == PtrLenUnknown || wanted_type->data.pointer.ptr_len == PtrLenC) &&
|
|
actual_type->id == ZigTypeIdPointer &&
|
|
actual_type->data.pointer.ptr_len == PtrLenSingle &&
|
|
actual_type->data.pointer.child_type->id == ZigTypeIdArray)
|
|
{
|
|
if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type, ResolveStatusAlignmentKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
if ((err = type_resolve(ira->codegen, wanted_type->data.pointer.child_type, ResolveStatusAlignmentKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
if (get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, wanted_type) &&
|
|
types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,
|
|
actual_type->data.pointer.child_type->data.array.child_type, source_node,
|
|
!wanted_type->data.pointer.is_const).id == ConstCastResultIdOk)
|
|
{
|
|
return ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value, wanted_type);
|
|
}
|
|
}
|
|
|
|
// *[N]T to []T
|
|
if (is_slice(wanted_type) &&
|
|
actual_type->id == ZigTypeIdPointer &&
|
|
actual_type->data.pointer.ptr_len == PtrLenSingle &&
|
|
actual_type->data.pointer.child_type->id == ZigTypeIdArray)
|
|
{
|
|
ZigType *slice_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
|
|
assert(slice_ptr_type->id == ZigTypeIdPointer);
|
|
ZigType *array_type = actual_type->data.pointer.child_type;
|
|
bool const_ok = (slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0
|
|
|| !actual_type->data.pointer.is_const);
|
|
if (const_ok && types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type,
|
|
array_type->data.array.child_type, source_node,
|
|
!slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk)
|
|
{
|
|
return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, wanted_type, result_loc);
|
|
}
|
|
}
|
|
|
|
// *@Frame(func) to anyframe->T or anyframe
|
|
if (actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle &&
|
|
actual_type->data.pointer.child_type->id == ZigTypeIdCoroFrame && wanted_type->id == ZigTypeIdAnyFrame)
|
|
{
|
|
bool ok = true;
|
|
if (wanted_type->data.any_frame.result_type != nullptr) {
|
|
ZigFn *fn = actual_type->data.pointer.child_type->data.frame.fn;
|
|
ZigType *fn_return_type = fn->type_entry->data.fn.fn_type_id.return_type;
|
|
if (wanted_type->data.any_frame.result_type != fn_return_type) {
|
|
ok = false;
|
|
}
|
|
}
|
|
if (ok) {
|
|
return ir_analyze_frame_ptr_to_anyframe(ira, source_instr, value, wanted_type);
|
|
}
|
|
}
|
|
|
|
// anyframe->T to anyframe
|
|
if (actual_type->id == ZigTypeIdAnyFrame && actual_type->data.any_frame.result_type != nullptr &&
|
|
wanted_type->id == ZigTypeIdAnyFrame && wanted_type->data.any_frame.result_type == nullptr)
|
|
{
|
|
return ir_analyze_anyframe_to_anyframe(ira, source_instr, value, wanted_type);
|
|
}
|
|
|
|
// cast from null literal to maybe type
|
|
if (wanted_type->id == ZigTypeIdOptional &&
|
|
actual_type->id == ZigTypeIdNull)
|
|
{
|
|
return ir_analyze_null_to_maybe(ira, source_instr, value, wanted_type);
|
|
}
|
|
|
|
// cast from null literal to C pointer
|
|
if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenC &&
|
|
actual_type->id == ZigTypeIdNull)
|
|
{
|
|
return ir_analyze_null_to_c_pointer(ira, source_instr, value, wanted_type);
|
|
}
|
|
|
|
// cast from [N]T to E![]const T
|
|
if (wanted_type->id == ZigTypeIdErrorUnion &&
|
|
is_slice(wanted_type->data.error_union.payload_type) &&
|
|
actual_type->id == ZigTypeIdArray)
|
|
{
|
|
ZigType *ptr_type =
|
|
wanted_type->data.error_union.payload_type->data.structure.fields[slice_ptr_index].type_entry;
|
|
assert(ptr_type->id == ZigTypeIdPointer);
|
|
if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
|
|
types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type,
|
|
source_node, false).id == ConstCastResultIdOk)
|
|
{
|
|
IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value, nullptr);
|
|
if (type_is_invalid(cast1->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1, result_loc);
|
|
if (type_is_invalid(cast2->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
return cast2;
|
|
}
|
|
}
|
|
|
|
// cast from E to E!T
|
|
if (wanted_type->id == ZigTypeIdErrorUnion &&
|
|
actual_type->id == ZigTypeIdErrorSet)
|
|
{
|
|
return ir_analyze_err_wrap_code(ira, source_instr, value, wanted_type, result_loc);
|
|
}
|
|
|
|
// cast from typed number to integer or float literal.
|
|
// works when the number is known at compile time
|
|
if (instr_is_comptime(value) &&
|
|
((actual_type->id == ZigTypeIdInt && wanted_type->id == ZigTypeIdComptimeInt) ||
|
|
(actual_type->id == ZigTypeIdFloat && wanted_type->id == ZigTypeIdComptimeFloat)))
|
|
{
|
|
return ir_analyze_number_to_literal(ira, source_instr, value, wanted_type);
|
|
}
|
|
|
|
// cast from enum literal to enum with matching field name
|
|
if (actual_type->id == ZigTypeIdEnumLiteral && wanted_type->id == ZigTypeIdEnum) {
|
|
if ((err = type_resolve(ira->codegen, wanted_type, ResolveStatusZeroBitsKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
TypeEnumField *field = find_enum_type_field(wanted_type, value->value.data.x_enum_literal);
|
|
if (field == nullptr) {
|
|
ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("enum '%s' has no field named '%s'",
|
|
buf_ptr(&wanted_type->name), buf_ptr(value->value.data.x_enum_literal)));
|
|
add_error_note(ira->codegen, msg, wanted_type->data.enumeration.decl_node,
|
|
buf_sprintf("'%s' declared here", buf_ptr(&wanted_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
IrInstruction *result = ir_const(ira, source_instr, wanted_type);
|
|
bigint_init_bigint(&result->value.data.x_enum_tag, &field->value);
|
|
return result;
|
|
}
|
|
|
|
// cast from union to the enum type of the union
|
|
if (actual_type->id == ZigTypeIdUnion && wanted_type->id == ZigTypeIdEnum) {
|
|
if ((err = type_resolve(ira->codegen, actual_type, ResolveStatusZeroBitsKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (actual_type->data.unionation.tag_type == wanted_type) {
|
|
return ir_analyze_union_to_tag(ira, source_instr, value, wanted_type);
|
|
}
|
|
}
|
|
|
|
// enum to union which has the enum as the tag type, or
|
|
// enum literal to union which has a matching enum as the tag type
|
|
if (is_tagged_union(wanted_type) && (actual_type->id == ZigTypeIdEnum ||
|
|
actual_type->id == ZigTypeIdEnumLiteral))
|
|
{
|
|
return ir_analyze_enum_to_union(ira, source_instr, value, wanted_type);
|
|
}
|
|
|
|
// cast from *T to *[1]T
|
|
if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle &&
|
|
actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle)
|
|
{
|
|
ZigType *array_type = wanted_type->data.pointer.child_type;
|
|
if (array_type->id == ZigTypeIdArray && array_type->data.array.len == 1 &&
|
|
types_match_const_cast_only(ira, array_type->data.array.child_type,
|
|
actual_type->data.pointer.child_type, source_node,
|
|
!wanted_type->data.pointer.is_const).id == ConstCastResultIdOk &&
|
|
// This should be the job of `types_match_const_cast_only`
|
|
// but `types_match_const_cast_only` only gets info for child_types
|
|
((wanted_type->data.pointer.is_const && actual_type->data.pointer.is_const) ||
|
|
!actual_type->data.pointer.is_const))
|
|
{
|
|
if ((err = type_resolve(ira->codegen, wanted_type->data.pointer.child_type,
|
|
ResolveStatusAlignmentKnown)))
|
|
{
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type,
|
|
ResolveStatusAlignmentKnown)))
|
|
{
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
uint32_t wanted_align = get_ptr_align(ira->codegen, wanted_type);
|
|
uint32_t actual_align = get_ptr_align(ira->codegen, actual_type);
|
|
if (wanted_align > actual_align) {
|
|
ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("cast increases pointer alignment"));
|
|
add_error_note(ira->codegen, msg, value->source_node,
|
|
buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&actual_type->name), actual_align));
|
|
add_error_note(ira->codegen, msg, source_instr->source_node,
|
|
buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&wanted_type->name), wanted_align));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
return ir_analyze_ptr_to_array(ira, source_instr, value, wanted_type);
|
|
}
|
|
}
|
|
|
|
// cast from *T and [*]T to *c_void and ?*c_void
|
|
// but don't do it if the actual type is a double pointer
|
|
if (is_pointery_and_elem_is_not_pointery(actual_type)) {
|
|
ZigType *dest_ptr_type = nullptr;
|
|
if (wanted_type->id == ZigTypeIdPointer &&
|
|
wanted_type->data.pointer.child_type == ira->codegen->builtin_types.entry_c_void)
|
|
{
|
|
dest_ptr_type = wanted_type;
|
|
} else if (wanted_type->id == ZigTypeIdOptional &&
|
|
wanted_type->data.maybe.child_type->id == ZigTypeIdPointer &&
|
|
wanted_type->data.maybe.child_type->data.pointer.child_type == ira->codegen->builtin_types.entry_c_void)
|
|
{
|
|
dest_ptr_type = wanted_type->data.maybe.child_type;
|
|
}
|
|
if (dest_ptr_type != nullptr) {
|
|
return ir_analyze_ptr_cast(ira, source_instr, value, wanted_type, source_instr, true);
|
|
}
|
|
}
|
|
|
|
// cast from T to *T where T is zero bits
|
|
if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle &&
|
|
types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,
|
|
actual_type, source_node, !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk)
|
|
{
|
|
if ((err = type_resolve(ira->codegen, actual_type, ResolveStatusZeroBitsKnown))) {
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
if (!type_has_bits(actual_type)) {
|
|
return ir_get_ref(ira, source_instr, value, false, false);
|
|
}
|
|
}
|
|
|
|
// cast from @Vector(N, T) to [N]T
|
|
if (wanted_type->id == ZigTypeIdArray && actual_type->id == ZigTypeIdVector &&
|
|
wanted_type->data.array.len == actual_type->data.vector.len &&
|
|
types_match_const_cast_only(ira, wanted_type->data.array.child_type,
|
|
actual_type->data.vector.elem_type, source_node, false).id == ConstCastResultIdOk)
|
|
{
|
|
return ir_analyze_vector_to_array(ira, source_instr, value, wanted_type, result_loc);
|
|
}
|
|
|
|
// cast from [N]T to @Vector(N, T)
|
|
if (actual_type->id == ZigTypeIdArray && wanted_type->id == ZigTypeIdVector &&
|
|
actual_type->data.array.len == wanted_type->data.vector.len &&
|
|
types_match_const_cast_only(ira, actual_type->data.array.child_type,
|
|
wanted_type->data.vector.elem_type, source_node, false).id == ConstCastResultIdOk)
|
|
{
|
|
return ir_analyze_array_to_vector(ira, source_instr, value, wanted_type);
|
|
}
|
|
|
|
// casting between C pointers and normal pointers
|
|
if (wanted_type->id == ZigTypeIdPointer && actual_type->id == ZigTypeIdPointer &&
|
|
(wanted_type->data.pointer.ptr_len == PtrLenC || actual_type->data.pointer.ptr_len == PtrLenC) &&
|
|
types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,
|
|
actual_type->data.pointer.child_type, source_node,
|
|
!wanted_type->data.pointer.is_const).id == ConstCastResultIdOk)
|
|
{
|
|
return ir_analyze_ptr_cast(ira, source_instr, value, wanted_type, source_instr, true);
|
|
}
|
|
|
|
// cast from integer to C pointer
|
|
if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenC &&
|
|
(actual_type->id == ZigTypeIdInt || actual_type->id == ZigTypeIdComptimeInt))
|
|
{
|
|
return ir_analyze_int_to_c_ptr(ira, source_instr, value, wanted_type);
|
|
}
|
|
|
|
// cast from undefined to anything
|
|
if (actual_type->id == ZigTypeIdUndefined) {
|
|
return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type);
|
|
}
|
|
|
|
ErrorMsg *parent_msg = ir_add_error_node(ira, source_instr->source_node,
|
|
buf_sprintf("expected type '%s', found '%s'",
|
|
buf_ptr(&wanted_type->name),
|
|
buf_ptr(&actual_type->name)));
|
|
report_recursive_error(ira, source_instr->source_node, &const_cast_result, parent_msg);
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
static IrInstruction *ir_implicit_cast_with_result(IrAnalyze *ira, IrInstruction *value, ZigType *expected_type,
|
|
ResultLoc *result_loc)
|
|
{
|
|
assert(value);
|
|
assert(value != ira->codegen->invalid_instruction);
|
|
assert(!expected_type || !type_is_invalid(expected_type));
|
|
assert(value->value.type);
|
|
assert(!type_is_invalid(value->value.type));
|
|
if (expected_type == nullptr)
|
|
return value; // anything will do
|
|
if (expected_type == value->value.type)
|
|
return value; // match
|
|
if (value->value.type->id == ZigTypeIdUnreachable)
|
|
return value;
|
|
|
|
return ir_analyze_cast(ira, value, expected_type, value, result_loc);
|
|
}
|
|
|
|
static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, ZigType *expected_type) {
|
|
return ir_implicit_cast_with_result(ira, value, expected_type, nullptr);
|
|
}
|
|
|
|
static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *ptr,
|
|
ResultLoc *result_loc)
|
|
{
|
|
Error err;
|
|
ZigType *type_entry = ptr->value.type;
|
|
if (type_is_invalid(type_entry))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (type_entry->id != ZigTypeIdPointer) {
|
|
ir_add_error_node(ira, source_instruction->source_node,
|
|
buf_sprintf("attempt to dereference non-pointer type '%s'",
|
|
buf_ptr(&type_entry->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
ZigType *child_type = type_entry->data.pointer.child_type;
|
|
// if the child type has one possible value, the deref is comptime
|
|
switch (type_has_one_possible_value(ira->codegen, child_type)) {
|
|
case OnePossibleValueInvalid:
|
|
return ira->codegen->invalid_instruction;
|
|
case OnePossibleValueYes:
|
|
return ir_const(ira, source_instruction, child_type);
|
|
case OnePossibleValueNo:
|
|
break;
|
|
}
|
|
if (instr_is_comptime(ptr)) {
|
|
if (ptr->value.special == ConstValSpecialUndef) {
|
|
ir_add_error(ira, ptr, buf_sprintf("attempt to dereference undefined value"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
if (ptr->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) {
|
|
ConstExprValue *pointee = const_ptr_pointee_unchecked(ira->codegen, &ptr->value);
|
|
if (pointee->special != ConstValSpecialRuntime) {
|
|
IrInstruction *result = ir_const(ira, source_instruction, child_type);
|
|
|
|
if ((err = ir_read_const_ptr(ira, ira->codegen, source_instruction->source_node, &result->value,
|
|
&ptr->value)))
|
|
{
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
result->value.type = child_type;
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
// if the instruction is a const ref instruction we can skip it
|
|
if (ptr->id == IrInstructionIdRef) {
|
|
IrInstructionRef *ref_inst = reinterpret_cast<IrInstructionRef *>(ptr);
|
|
return ref_inst->value;
|
|
}
|
|
|
|
IrInstruction *result_loc_inst;
|
|
if (type_entry->data.pointer.host_int_bytes != 0 && handle_is_ptr(child_type)) {
|
|
if (result_loc == nullptr) result_loc = no_result_loc();
|
|
result_loc_inst = ir_resolve_result(ira, source_instruction, result_loc, child_type, nullptr,
|
|
true, false, true);
|
|
if (type_is_invalid(result_loc_inst->value.type) || instr_is_unreachable(result_loc_inst)) {
|
|
return result_loc_inst;
|
|
}
|
|
} else {
|
|
result_loc_inst = nullptr;
|
|
}
|
|
|
|
return ir_build_load_ptr_gen(ira, source_instruction, ptr, child_type, result_loc_inst);
|
|
}
|
|
|
|
static bool ir_resolve_align(IrAnalyze *ira, IrInstruction *value, uint32_t *out) {
|
|
if (type_is_invalid(value->value.type))
|
|
return false;
|
|
|
|
IrInstruction *casted_value = ir_implicit_cast(ira, value, get_align_amt_type(ira->codegen));
|
|
if (type_is_invalid(casted_value->value.type))
|
|
return false;
|
|
|
|
ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad);
|
|
if (!const_val)
|
|
return false;
|
|
|
|
uint32_t align_bytes = bigint_as_unsigned(&const_val->data.x_bigint);
|
|
if (align_bytes == 0) {
|
|
ir_add_error(ira, value, buf_sprintf("alignment must be >= 1"));
|
|
return false;
|
|
}
|
|
|
|
if (!is_power_of_2(align_bytes)) {
|
|
ir_add_error(ira, value, buf_sprintf("alignment value %" PRIu32 " is not a power of 2", align_bytes));
|
|
return false;
|
|
}
|
|
|
|
*out = align_bytes;
|
|
return true;
|
|
}
|
|
|
|
static bool ir_resolve_unsigned(IrAnalyze *ira, IrInstruction *value, ZigType *int_type, uint64_t *out) {
|
|
if (type_is_invalid(value->value.type))
|
|
return false;
|
|
|
|
IrInstruction *casted_value = ir_implicit_cast(ira, value, int_type);
|
|
if (type_is_invalid(casted_value->value.type))
|
|
return false;
|
|
|
|
ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad);
|
|
if (!const_val)
|
|
return false;
|
|
|
|
*out = bigint_as_unsigned(&const_val->data.x_bigint);
|
|
return true;
|
|
}
|
|
|
|
static bool ir_resolve_usize(IrAnalyze *ira, IrInstruction *value, uint64_t *out) {
|
|
return ir_resolve_unsigned(ira, value, ira->codegen->builtin_types.entry_usize, out);
|
|
}
|
|
|
|
static bool ir_resolve_bool(IrAnalyze *ira, IrInstruction *value, bool *out) {
|
|
if (type_is_invalid(value->value.type))
|
|
return false;
|
|
|
|
IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->codegen->builtin_types.entry_bool);
|
|
if (type_is_invalid(casted_value->value.type))
|
|
return false;
|
|
|
|
ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad);
|
|
if (!const_val)
|
|
return false;
|
|
|
|
*out = const_val->data.x_bool;
|
|
return true;
|
|
}
|
|
|
|
static bool ir_resolve_comptime(IrAnalyze *ira, IrInstruction *value, bool *out) {
|
|
if (!value) {
|
|
*out = false;
|
|
return true;
|
|
}
|
|
return ir_resolve_bool(ira, value, out);
|
|
}
|
|
|
|
static bool ir_resolve_atomic_order(IrAnalyze *ira, IrInstruction *value, AtomicOrder *out) {
|
|
if (type_is_invalid(value->value.type))
|
|
return false;
|
|
|
|
ConstExprValue *atomic_order_val = get_builtin_value(ira->codegen, "AtomicOrder");
|
|
assert(atomic_order_val->type->id == ZigTypeIdMetaType);
|
|
ZigType *atomic_order_type = atomic_order_val->data.x_type;
|
|
|
|
IrInstruction *casted_value = ir_implicit_cast(ira, value, atomic_order_type);
|
|
if (type_is_invalid(casted_value->value.type))
|
|
return false;
|
|
|
|
ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad);
|
|
if (!const_val)
|
|
return false;
|
|
|
|
*out = (AtomicOrder)bigint_as_unsigned(&const_val->data.x_enum_tag);
|
|
return true;
|
|
}
|
|
|
|
static bool ir_resolve_atomic_rmw_op(IrAnalyze *ira, IrInstruction *value, AtomicRmwOp *out) {
|
|
if (type_is_invalid(value->value.type))
|
|
return false;
|
|
|
|
ConstExprValue *atomic_rmw_op_val = get_builtin_value(ira->codegen, "AtomicRmwOp");
|
|
assert(atomic_rmw_op_val->type->id == ZigTypeIdMetaType);
|
|
ZigType *atomic_rmw_op_type = atomic_rmw_op_val->data.x_type;
|
|
|
|
IrInstruction *casted_value = ir_implicit_cast(ira, value, atomic_rmw_op_type);
|
|
if (type_is_invalid(casted_value->value.type))
|
|
return false;
|
|
|
|
ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad);
|
|
if (!const_val)
|
|
return false;
|
|
|
|
*out = (AtomicRmwOp)bigint_as_unsigned(&const_val->data.x_enum_tag);
|
|
return true;
|
|
}
|
|
|
|
static bool ir_resolve_global_linkage(IrAnalyze *ira, IrInstruction *value, GlobalLinkageId *out) {
|
|
if (type_is_invalid(value->value.type))
|
|
return false;
|
|
|
|
ConstExprValue *global_linkage_val = get_builtin_value(ira->codegen, "GlobalLinkage");
|
|
assert(global_linkage_val->type->id == ZigTypeIdMetaType);
|
|
ZigType *global_linkage_type = global_linkage_val->data.x_type;
|
|
|
|
IrInstruction *casted_value = ir_implicit_cast(ira, value, global_linkage_type);
|
|
if (type_is_invalid(casted_value->value.type))
|
|
return false;
|
|
|
|
ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad);
|
|
if (!const_val)
|
|
return false;
|
|
|
|
*out = (GlobalLinkageId)bigint_as_unsigned(&const_val->data.x_enum_tag);
|
|
return true;
|
|
}
|
|
|
|
static bool ir_resolve_float_mode(IrAnalyze *ira, IrInstruction *value, FloatMode *out) {
|
|
if (type_is_invalid(value->value.type))
|
|
return false;
|
|
|
|
ConstExprValue *float_mode_val = get_builtin_value(ira->codegen, "FloatMode");
|
|
assert(float_mode_val->type->id == ZigTypeIdMetaType);
|
|
ZigType *float_mode_type = float_mode_val->data.x_type;
|
|
|
|
IrInstruction *casted_value = ir_implicit_cast(ira, value, float_mode_type);
|
|
if (type_is_invalid(casted_value->value.type))
|
|
return false;
|
|
|
|
ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad);
|
|
if (!const_val)
|
|
return false;
|
|
|
|
*out = (FloatMode)bigint_as_unsigned(&const_val->data.x_enum_tag);
|
|
return true;
|
|
}
|
|
|
|
|
|
static Buf *ir_resolve_str(IrAnalyze *ira, IrInstruction *value) {
|
|
if (type_is_invalid(value->value.type))
|
|
return nullptr;
|
|
|
|
ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,
|
|
true, false, PtrLenUnknown, 0, 0, 0, false);
|
|
ZigType *str_type = get_slice_type(ira->codegen, ptr_type);
|
|
IrInstruction *casted_value = ir_implicit_cast(ira, value, str_type);
|
|
if (type_is_invalid(casted_value->value.type))
|
|
return nullptr;
|
|
|
|
ConstExprValue *const_val = ir_resolve_const(ira, casted_value, UndefBad);
|
|
if (!const_val)
|
|
return nullptr;
|
|
|
|
ConstExprValue *ptr_field = &const_val->data.x_struct.fields[slice_ptr_index];
|
|
ConstExprValue *len_field = &const_val->data.x_struct.fields[slice_len_index];
|
|
|
|
assert(ptr_field->data.x_ptr.special == ConstPtrSpecialBaseArray);
|
|
ConstExprValue *array_val = ptr_field->data.x_ptr.data.base_array.array_val;
|
|
if (array_val->data.x_array.special == ConstArraySpecialBuf) {
|
|
return array_val->data.x_array.data.s_buf;
|
|
}
|
|
expand_undef_array(ira->codegen, array_val);
|
|
size_t len = bigint_as_unsigned(&len_field->data.x_bigint);
|
|
Buf *result = buf_alloc();
|
|
buf_resize(result, len);
|
|
for (size_t i = 0; i < len; i += 1) {
|
|
size_t new_index = ptr_field->data.x_ptr.data.base_array.elem_index + i;
|
|
ConstExprValue *char_val = &array_val->data.x_array.data.s_none.elements[new_index];
|
|
if (char_val->special == ConstValSpecialUndef) {
|
|
ir_add_error(ira, casted_value, buf_sprintf("use of undefined value"));
|
|
return nullptr;
|
|
}
|
|
uint64_t big_c = bigint_as_unsigned(&char_val->data.x_bigint);
|
|
assert(big_c <= UINT8_MAX);
|
|
uint8_t c = (uint8_t)big_c;
|
|
buf_ptr(result)[i] = c;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_add_implicit_return_type(IrAnalyze *ira,
|
|
IrInstructionAddImplicitReturnType *instruction)
|
|
{
|
|
IrInstruction *value = instruction->value->child;
|
|
if (type_is_invalid(value->value.type))
|
|
return ir_unreach_error(ira);
|
|
|
|
ira->src_implicit_return_type_list.append(value);
|
|
|
|
return ir_const_void(ira, &instruction->base);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_return_begin(IrAnalyze *ira, IrInstructionReturnBegin *instruction) {
|
|
IrInstruction *operand = instruction->operand->child;
|
|
if (type_is_invalid(operand->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *casted_operand = ir_implicit_cast(ira, operand, ira->explicit_return_type);
|
|
if (type_is_invalid(casted_operand->value.type)) {
|
|
AstNode *source_node = ira->explicit_return_type_source_node;
|
|
if (source_node != nullptr) {
|
|
ErrorMsg *msg = ira->codegen->errors.last();
|
|
add_error_note(ira->codegen, msg, source_node,
|
|
buf_sprintf("return type declared here"));
|
|
}
|
|
return ir_unreach_error(ira);
|
|
}
|
|
|
|
return ir_build_return_begin(&ira->new_irb, instruction->base.scope, instruction->base.source_node,
|
|
casted_operand);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_return(IrAnalyze *ira, IrInstructionReturn *instruction) {
|
|
IrInstruction *operand = instruction->operand->child;
|
|
if (type_is_invalid(operand->value.type))
|
|
return ir_unreach_error(ira);
|
|
|
|
if (!instr_is_comptime(operand) && handle_is_ptr(ira->explicit_return_type)) {
|
|
// result location mechanism took care of it.
|
|
IrInstruction *result = ir_build_return(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node, nullptr);
|
|
result->value.type = ira->codegen->builtin_types.entry_unreachable;
|
|
return ir_finish_anal(ira, result);
|
|
}
|
|
|
|
IrInstruction *casted_operand = ir_implicit_cast(ira, operand, ira->explicit_return_type);
|
|
if (type_is_invalid(casted_operand->value.type)) {
|
|
// error already reported by IrInstructionReturnBegin
|
|
return ir_unreach_error(ira);
|
|
}
|
|
|
|
if (casted_operand->value.special == ConstValSpecialRuntime &&
|
|
casted_operand->value.type->id == ZigTypeIdPointer &&
|
|
casted_operand->value.data.rh_ptr == RuntimeHintPtrStack)
|
|
{
|
|
ir_add_error(ira, casted_operand, buf_sprintf("function returns address of local variable"));
|
|
return ir_unreach_error(ira);
|
|
}
|
|
IrInstruction *result = ir_build_return(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node, casted_operand);
|
|
result->value.type = ira->codegen->builtin_types.entry_unreachable;
|
|
return ir_finish_anal(ira, result);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_const(IrAnalyze *ira, IrInstructionConst *instruction) {
|
|
IrInstruction *result = ir_const(ira, &instruction->base, nullptr);
|
|
copy_const_val(&result->value, &instruction->base.value, true);
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_bin_op_bool(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
|
|
IrInstruction *op1 = bin_op_instruction->op1->child;
|
|
if (type_is_invalid(op1->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *op2 = bin_op_instruction->op2->child;
|
|
if (type_is_invalid(op2->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *bool_type = ira->codegen->builtin_types.entry_bool;
|
|
|
|
IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, bool_type);
|
|
if (casted_op1 == ira->codegen->invalid_instruction)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, bool_type);
|
|
if (casted_op2 == ira->codegen->invalid_instruction)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2)) {
|
|
ConstExprValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad);
|
|
if (op1_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad);
|
|
if (op2_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
assert(casted_op1->value.type->id == ZigTypeIdBool);
|
|
assert(casted_op2->value.type->id == ZigTypeIdBool);
|
|
bool result_bool;
|
|
if (bin_op_instruction->op_id == IrBinOpBoolOr) {
|
|
result_bool = op1_val->data.x_bool || op2_val->data.x_bool;
|
|
} else if (bin_op_instruction->op_id == IrBinOpBoolAnd) {
|
|
result_bool = op1_val->data.x_bool && op2_val->data.x_bool;
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
return ir_const_bool(ira, &bin_op_instruction->base, result_bool);
|
|
}
|
|
|
|
IrInstruction *result = ir_build_bin_op(&ira->new_irb,
|
|
bin_op_instruction->base.scope, bin_op_instruction->base.source_node,
|
|
bin_op_instruction->op_id, casted_op1, casted_op2, bin_op_instruction->safety_check_on);
|
|
result->value.type = bool_type;
|
|
return result;
|
|
}
|
|
|
|
static bool resolve_cmp_op_id(IrBinOp op_id, Cmp cmp) {
|
|
switch (op_id) {
|
|
case IrBinOpCmpEq:
|
|
return cmp == CmpEQ;
|
|
case IrBinOpCmpNotEq:
|
|
return cmp != CmpEQ;
|
|
case IrBinOpCmpLessThan:
|
|
return cmp == CmpLT;
|
|
case IrBinOpCmpGreaterThan:
|
|
return cmp == CmpGT;
|
|
case IrBinOpCmpLessOrEq:
|
|
return cmp != CmpGT;
|
|
case IrBinOpCmpGreaterOrEq:
|
|
return cmp != CmpLT;
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
|
|
static bool optional_value_is_null(ConstExprValue *val) {
|
|
assert(val->special == ConstValSpecialStatic);
|
|
if (get_codegen_ptr_type(val->type) != nullptr) {
|
|
if (val->data.x_ptr.special == ConstPtrSpecialNull) {
|
|
return true;
|
|
} else if (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) {
|
|
return val->data.x_ptr.data.hard_coded_addr.addr == 0;
|
|
} else {
|
|
return false;
|
|
}
|
|
} else if (is_opt_err_set(val->type)) {
|
|
return val->data.x_err_set == nullptr;
|
|
} else {
|
|
return val->data.x_optional == nullptr;
|
|
}
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
|
|
IrInstruction *op1 = bin_op_instruction->op1->child;
|
|
if (type_is_invalid(op1->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *op2 = bin_op_instruction->op2->child;
|
|
if (type_is_invalid(op2->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
AstNode *source_node = bin_op_instruction->base.source_node;
|
|
|
|
IrBinOp op_id = bin_op_instruction->op_id;
|
|
bool is_equality_cmp = (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq);
|
|
if (is_equality_cmp && op1->value.type->id == ZigTypeIdNull && op2->value.type->id == ZigTypeIdNull) {
|
|
return ir_const_bool(ira, &bin_op_instruction->base, (op_id == IrBinOpCmpEq));
|
|
} else if (is_equality_cmp &&
|
|
((op1->value.type->id == ZigTypeIdNull && op2->value.type->id == ZigTypeIdOptional) ||
|
|
(op2->value.type->id == ZigTypeIdNull && op1->value.type->id == ZigTypeIdOptional)))
|
|
{
|
|
IrInstruction *maybe_op;
|
|
if (op1->value.type->id == ZigTypeIdNull) {
|
|
maybe_op = op2;
|
|
} else if (op2->value.type->id == ZigTypeIdNull) {
|
|
maybe_op = op1;
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
if (instr_is_comptime(maybe_op)) {
|
|
ConstExprValue *maybe_val = ir_resolve_const(ira, maybe_op, UndefBad);
|
|
if (!maybe_val)
|
|
return ira->codegen->invalid_instruction;
|
|
bool is_null = optional_value_is_null(maybe_val);
|
|
bool bool_result = (op_id == IrBinOpCmpEq) ? is_null : !is_null;
|
|
return ir_const_bool(ira, &bin_op_instruction->base, bool_result);
|
|
}
|
|
|
|
IrInstruction *is_non_null = ir_build_test_nonnull(&ira->new_irb, bin_op_instruction->base.scope,
|
|
source_node, maybe_op);
|
|
is_non_null->value.type = ira->codegen->builtin_types.entry_bool;
|
|
|
|
if (op_id == IrBinOpCmpEq) {
|
|
IrInstruction *result = ir_build_bool_not(&ira->new_irb, bin_op_instruction->base.scope,
|
|
bin_op_instruction->base.source_node, is_non_null);
|
|
result->value.type = ira->codegen->builtin_types.entry_bool;
|
|
return result;
|
|
} else {
|
|
return is_non_null;
|
|
}
|
|
} else if (is_equality_cmp &&
|
|
((op1->value.type->id == ZigTypeIdNull && op2->value.type->id == ZigTypeIdPointer &&
|
|
op2->value.type->data.pointer.ptr_len == PtrLenC) ||
|
|
(op2->value.type->id == ZigTypeIdNull && op1->value.type->id == ZigTypeIdPointer &&
|
|
op1->value.type->data.pointer.ptr_len == PtrLenC)))
|
|
{
|
|
IrInstruction *c_ptr_op;
|
|
if (op1->value.type->id == ZigTypeIdNull) {
|
|
c_ptr_op = op2;
|
|
} else if (op2->value.type->id == ZigTypeIdNull) {
|
|
c_ptr_op = op1;
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
if (instr_is_comptime(c_ptr_op)) {
|
|
ConstExprValue *c_ptr_val = ir_resolve_const(ira, c_ptr_op, UndefOk);
|
|
if (!c_ptr_val)
|
|
return ira->codegen->invalid_instruction;
|
|
if (c_ptr_val->special == ConstValSpecialUndef)
|
|
return ir_const_undef(ira, &bin_op_instruction->base, ira->codegen->builtin_types.entry_bool);
|
|
bool is_null = c_ptr_val->data.x_ptr.special == ConstPtrSpecialNull ||
|
|
(c_ptr_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr &&
|
|
c_ptr_val->data.x_ptr.data.hard_coded_addr.addr == 0);
|
|
bool bool_result = (op_id == IrBinOpCmpEq) ? is_null : !is_null;
|
|
return ir_const_bool(ira, &bin_op_instruction->base, bool_result);
|
|
}
|
|
IrInstruction *is_non_null = ir_build_test_nonnull(&ira->new_irb, bin_op_instruction->base.scope,
|
|
source_node, c_ptr_op);
|
|
is_non_null->value.type = ira->codegen->builtin_types.entry_bool;
|
|
|
|
if (op_id == IrBinOpCmpEq) {
|
|
IrInstruction *result = ir_build_bool_not(&ira->new_irb, bin_op_instruction->base.scope,
|
|
bin_op_instruction->base.source_node, is_non_null);
|
|
result->value.type = ira->codegen->builtin_types.entry_bool;
|
|
return result;
|
|
} else {
|
|
return is_non_null;
|
|
}
|
|
} else if (op1->value.type->id == ZigTypeIdNull || op2->value.type->id == ZigTypeIdNull) {
|
|
ZigType *non_null_type = (op1->value.type->id == ZigTypeIdNull) ? op2->value.type : op1->value.type;
|
|
ir_add_error_node(ira, source_node, buf_sprintf("comparison of '%s' with null",
|
|
buf_ptr(&non_null_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (op1->value.type->id == ZigTypeIdErrorSet && op2->value.type->id == ZigTypeIdErrorSet) {
|
|
if (!is_equality_cmp) {
|
|
ir_add_error_node(ira, source_node, buf_sprintf("operator not allowed for errors"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
ZigType *intersect_type = get_error_set_intersection(ira, op1->value.type, op2->value.type, source_node);
|
|
if (type_is_invalid(intersect_type)) {
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (!resolve_inferred_error_set(ira->codegen, intersect_type, source_node)) {
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
// exception if one of the operators has the type of the empty error set, we allow the comparison
|
|
// (and make it comptime known)
|
|
// this is a function which is evaluated at comptime and returns an inferred error set will have an empty
|
|
// error set.
|
|
if (op1->value.type->data.error_set.err_count == 0 || op2->value.type->data.error_set.err_count == 0) {
|
|
bool are_equal = false;
|
|
bool answer;
|
|
if (op_id == IrBinOpCmpEq) {
|
|
answer = are_equal;
|
|
} else if (op_id == IrBinOpCmpNotEq) {
|
|
answer = !are_equal;
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
return ir_const_bool(ira, &bin_op_instruction->base, answer);
|
|
}
|
|
|
|
if (!type_is_global_error_set(intersect_type)) {
|
|
if (intersect_type->data.error_set.err_count == 0) {
|
|
ir_add_error_node(ira, source_node,
|
|
buf_sprintf("error sets '%s' and '%s' have no common errors",
|
|
buf_ptr(&op1->value.type->name), buf_ptr(&op2->value.type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
if (op1->value.type->data.error_set.err_count == 1 && op2->value.type->data.error_set.err_count == 1) {
|
|
bool are_equal = true;
|
|
bool answer;
|
|
if (op_id == IrBinOpCmpEq) {
|
|
answer = are_equal;
|
|
} else if (op_id == IrBinOpCmpNotEq) {
|
|
answer = !are_equal;
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
return ir_const_bool(ira, &bin_op_instruction->base, answer);
|
|
}
|
|
}
|
|
|
|
if (instr_is_comptime(op1) && instr_is_comptime(op2)) {
|
|
ConstExprValue *op1_val = ir_resolve_const(ira, op1, UndefBad);
|
|
if (op1_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
ConstExprValue *op2_val = ir_resolve_const(ira, op2, UndefBad);
|
|
if (op2_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
bool answer;
|
|
bool are_equal = op1_val->data.x_err_set->value == op2_val->data.x_err_set->value;
|
|
if (op_id == IrBinOpCmpEq) {
|
|
answer = are_equal;
|
|
} else if (op_id == IrBinOpCmpNotEq) {
|
|
answer = !are_equal;
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
|
|
return ir_const_bool(ira, &bin_op_instruction->base, answer);
|
|
}
|
|
|
|
IrInstruction *result = ir_build_bin_op(&ira->new_irb,
|
|
bin_op_instruction->base.scope, bin_op_instruction->base.source_node,
|
|
op_id, op1, op2, bin_op_instruction->safety_check_on);
|
|
result->value.type = ira->codegen->builtin_types.entry_bool;
|
|
return result;
|
|
}
|
|
|
|
IrInstruction *instructions[] = {op1, op2};
|
|
ZigType *resolved_type = ir_resolve_peer_types(ira, source_node, nullptr, instructions, 2);
|
|
if (type_is_invalid(resolved_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
bool operator_allowed;
|
|
switch (resolved_type->id) {
|
|
case ZigTypeIdInvalid:
|
|
zig_unreachable(); // handled above
|
|
|
|
case ZigTypeIdComptimeFloat:
|
|
case ZigTypeIdComptimeInt:
|
|
case ZigTypeIdInt:
|
|
case ZigTypeIdFloat:
|
|
case ZigTypeIdVector:
|
|
operator_allowed = true;
|
|
break;
|
|
|
|
case ZigTypeIdBool:
|
|
case ZigTypeIdMetaType:
|
|
case ZigTypeIdVoid:
|
|
case ZigTypeIdErrorSet:
|
|
case ZigTypeIdFn:
|
|
case ZigTypeIdOpaque:
|
|
case ZigTypeIdBoundFn:
|
|
case ZigTypeIdArgTuple:
|
|
case ZigTypeIdEnum:
|
|
case ZigTypeIdEnumLiteral:
|
|
case ZigTypeIdAnyFrame:
|
|
operator_allowed = is_equality_cmp;
|
|
break;
|
|
|
|
case ZigTypeIdPointer:
|
|
operator_allowed = is_equality_cmp || (resolved_type->data.pointer.ptr_len == PtrLenC);
|
|
break;
|
|
|
|
case ZigTypeIdUnreachable:
|
|
case ZigTypeIdArray:
|
|
case ZigTypeIdStruct:
|
|
case ZigTypeIdUndefined:
|
|
case ZigTypeIdNull:
|
|
case ZigTypeIdErrorUnion:
|
|
case ZigTypeIdUnion:
|
|
case ZigTypeIdCoroFrame:
|
|
operator_allowed = false;
|
|
break;
|
|
case ZigTypeIdOptional:
|
|
operator_allowed = is_equality_cmp && get_codegen_ptr_type(resolved_type) != nullptr;
|
|
break;
|
|
}
|
|
if (!operator_allowed) {
|
|
ir_add_error_node(ira, source_node,
|
|
buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, resolved_type);
|
|
if (casted_op1 == ira->codegen->invalid_instruction)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, resolved_type);
|
|
if (casted_op2 == ira->codegen->invalid_instruction)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
bool one_possible_value;
|
|
switch (type_has_one_possible_value(ira->codegen, resolved_type)) {
|
|
case OnePossibleValueInvalid:
|
|
return ira->codegen->invalid_instruction;
|
|
case OnePossibleValueYes:
|
|
one_possible_value = true;
|
|
break;
|
|
case OnePossibleValueNo:
|
|
one_possible_value = false;
|
|
break;
|
|
}
|
|
|
|
if (one_possible_value || (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2))) {
|
|
ConstExprValue *op1_val = one_possible_value ? &casted_op1->value : ir_resolve_const(ira, casted_op1, UndefBad);
|
|
if (op1_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
ConstExprValue *op2_val = one_possible_value ? &casted_op2->value : ir_resolve_const(ira, casted_op2, UndefBad);
|
|
if (op2_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (resolved_type->id == ZigTypeIdComptimeFloat || resolved_type->id == ZigTypeIdFloat) {
|
|
if (float_is_nan(op1_val) || float_is_nan(op2_val)) {
|
|
return ir_const_bool(ira, &bin_op_instruction->base, op_id == IrBinOpCmpNotEq);
|
|
}
|
|
Cmp cmp_result = float_cmp(op1_val, op2_val);
|
|
bool answer = resolve_cmp_op_id(op_id, cmp_result);
|
|
return ir_const_bool(ira, &bin_op_instruction->base, answer);
|
|
} else if (resolved_type->id == ZigTypeIdComptimeInt || resolved_type->id == ZigTypeIdInt) {
|
|
Cmp cmp_result = bigint_cmp(&op1_val->data.x_bigint, &op2_val->data.x_bigint);
|
|
bool answer = resolve_cmp_op_id(op_id, cmp_result);
|
|
return ir_const_bool(ira, &bin_op_instruction->base, answer);
|
|
} else if (resolved_type->id == ZigTypeIdPointer && op_id != IrBinOpCmpEq && op_id != IrBinOpCmpNotEq) {
|
|
if ((op1_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr ||
|
|
op1_val->data.x_ptr.special == ConstPtrSpecialNull) &&
|
|
(op2_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr ||
|
|
op2_val->data.x_ptr.special == ConstPtrSpecialNull))
|
|
{
|
|
uint64_t op1_addr = op1_val->data.x_ptr.special == ConstPtrSpecialNull ?
|
|
0 : op1_val->data.x_ptr.data.hard_coded_addr.addr;
|
|
uint64_t op2_addr = op2_val->data.x_ptr.special == ConstPtrSpecialNull ?
|
|
0 : op2_val->data.x_ptr.data.hard_coded_addr.addr;
|
|
Cmp cmp_result;
|
|
if (op1_addr > op2_addr) {
|
|
cmp_result = CmpGT;
|
|
} else if (op1_addr < op2_addr) {
|
|
cmp_result = CmpLT;
|
|
} else {
|
|
cmp_result = CmpEQ;
|
|
}
|
|
bool answer = resolve_cmp_op_id(op_id, cmp_result);
|
|
return ir_const_bool(ira, &bin_op_instruction->base, answer);
|
|
}
|
|
} else {
|
|
bool are_equal = one_possible_value || const_values_equal(ira->codegen, op1_val, op2_val);
|
|
bool answer;
|
|
if (op_id == IrBinOpCmpEq) {
|
|
answer = are_equal;
|
|
} else if (op_id == IrBinOpCmpNotEq) {
|
|
answer = !are_equal;
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
return ir_const_bool(ira, &bin_op_instruction->base, answer);
|
|
}
|
|
}
|
|
|
|
// some comparisons with unsigned numbers can be evaluated
|
|
if (resolved_type->id == ZigTypeIdInt && !resolved_type->data.integral.is_signed) {
|
|
ConstExprValue *known_left_val;
|
|
IrBinOp flipped_op_id;
|
|
if (instr_is_comptime(casted_op1)) {
|
|
known_left_val = ir_resolve_const(ira, casted_op1, UndefBad);
|
|
if (known_left_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
flipped_op_id = op_id;
|
|
} else if (instr_is_comptime(casted_op2)) {
|
|
known_left_val = ir_resolve_const(ira, casted_op2, UndefBad);
|
|
if (known_left_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (op_id == IrBinOpCmpLessThan) {
|
|
flipped_op_id = IrBinOpCmpGreaterThan;
|
|
} else if (op_id == IrBinOpCmpGreaterThan) {
|
|
flipped_op_id = IrBinOpCmpLessThan;
|
|
} else if (op_id == IrBinOpCmpLessOrEq) {
|
|
flipped_op_id = IrBinOpCmpGreaterOrEq;
|
|
} else if (op_id == IrBinOpCmpGreaterOrEq) {
|
|
flipped_op_id = IrBinOpCmpLessOrEq;
|
|
} else {
|
|
flipped_op_id = op_id;
|
|
}
|
|
} else {
|
|
known_left_val = nullptr;
|
|
}
|
|
if (known_left_val != nullptr && bigint_cmp_zero(&known_left_val->data.x_bigint) == CmpEQ &&
|
|
(flipped_op_id == IrBinOpCmpLessOrEq || flipped_op_id == IrBinOpCmpGreaterThan))
|
|
{
|
|
bool answer = (flipped_op_id == IrBinOpCmpLessOrEq);
|
|
return ir_const_bool(ira, &bin_op_instruction->base, answer);
|
|
}
|
|
}
|
|
|
|
IrInstruction *result = ir_build_bin_op(&ira->new_irb,
|
|
bin_op_instruction->base.scope, bin_op_instruction->base.source_node,
|
|
op_id, casted_op1, casted_op2, bin_op_instruction->safety_check_on);
|
|
result->value.type = ira->codegen->builtin_types.entry_bool;
|
|
return result;
|
|
}
|
|
|
|
static ErrorMsg *ir_eval_math_op_scalar(IrAnalyze *ira, IrInstruction *source_instr, ZigType *type_entry,
|
|
ConstExprValue *op1_val, IrBinOp op_id, ConstExprValue *op2_val, ConstExprValue *out_val)
|
|
{
|
|
bool is_int;
|
|
bool is_float;
|
|
Cmp op2_zcmp;
|
|
if (type_entry->id == ZigTypeIdInt || type_entry->id == ZigTypeIdComptimeInt) {
|
|
is_int = true;
|
|
is_float = false;
|
|
op2_zcmp = bigint_cmp_zero(&op2_val->data.x_bigint);
|
|
} else if (type_entry->id == ZigTypeIdFloat ||
|
|
type_entry->id == ZigTypeIdComptimeFloat)
|
|
{
|
|
is_int = false;
|
|
is_float = true;
|
|
op2_zcmp = float_cmp_zero(op2_val);
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
|
|
if ((op_id == IrBinOpDivUnspecified || op_id == IrBinOpRemRem || op_id == IrBinOpRemMod ||
|
|
op_id == IrBinOpDivTrunc || op_id == IrBinOpDivFloor) && op2_zcmp == CmpEQ)
|
|
{
|
|
return ir_add_error(ira, source_instr, buf_sprintf("division by zero"));
|
|
}
|
|
if ((op_id == IrBinOpRemRem || op_id == IrBinOpRemMod) && op2_zcmp == CmpLT) {
|
|
return ir_add_error(ira, source_instr, buf_sprintf("negative denominator"));
|
|
}
|
|
|
|
switch (op_id) {
|
|
case IrBinOpInvalid:
|
|
case IrBinOpBoolOr:
|
|
case IrBinOpBoolAnd:
|
|
case IrBinOpCmpEq:
|
|
case IrBinOpCmpNotEq:
|
|
case IrBinOpCmpLessThan:
|
|
case IrBinOpCmpGreaterThan:
|
|
case IrBinOpCmpLessOrEq:
|
|
case IrBinOpCmpGreaterOrEq:
|
|
case IrBinOpArrayCat:
|
|
case IrBinOpArrayMult:
|
|
case IrBinOpRemUnspecified:
|
|
case IrBinOpMergeErrorSets:
|
|
zig_unreachable();
|
|
case IrBinOpBinOr:
|
|
assert(is_int);
|
|
bigint_or(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
|
|
break;
|
|
case IrBinOpBinXor:
|
|
assert(is_int);
|
|
bigint_xor(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
|
|
break;
|
|
case IrBinOpBinAnd:
|
|
assert(is_int);
|
|
bigint_and(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
|
|
break;
|
|
case IrBinOpBitShiftLeftExact:
|
|
assert(is_int);
|
|
bigint_shl(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
|
|
break;
|
|
case IrBinOpBitShiftLeftLossy:
|
|
assert(type_entry->id == ZigTypeIdInt);
|
|
bigint_shl_trunc(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint,
|
|
type_entry->data.integral.bit_count, type_entry->data.integral.is_signed);
|
|
break;
|
|
case IrBinOpBitShiftRightExact:
|
|
{
|
|
assert(is_int);
|
|
bigint_shr(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
|
|
BigInt orig_bigint;
|
|
bigint_shl(&orig_bigint, &out_val->data.x_bigint, &op2_val->data.x_bigint);
|
|
if (bigint_cmp(&op1_val->data.x_bigint, &orig_bigint) != CmpEQ) {
|
|
return ir_add_error(ira, source_instr, buf_sprintf("exact shift shifted out 1 bits"));
|
|
}
|
|
break;
|
|
}
|
|
case IrBinOpBitShiftRightLossy:
|
|
assert(is_int);
|
|
bigint_shr(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
|
|
break;
|
|
case IrBinOpAdd:
|
|
if (is_int) {
|
|
bigint_add(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
|
|
} else {
|
|
float_add(out_val, op1_val, op2_val);
|
|
}
|
|
break;
|
|
case IrBinOpAddWrap:
|
|
assert(type_entry->id == ZigTypeIdInt);
|
|
bigint_add_wrap(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint,
|
|
type_entry->data.integral.bit_count, type_entry->data.integral.is_signed);
|
|
break;
|
|
case IrBinOpSub:
|
|
if (is_int) {
|
|
bigint_sub(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
|
|
} else {
|
|
float_sub(out_val, op1_val, op2_val);
|
|
}
|
|
break;
|
|
case IrBinOpSubWrap:
|
|
assert(type_entry->id == ZigTypeIdInt);
|
|
bigint_sub_wrap(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint,
|
|
type_entry->data.integral.bit_count, type_entry->data.integral.is_signed);
|
|
break;
|
|
case IrBinOpMult:
|
|
if (is_int) {
|
|
bigint_mul(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
|
|
} else {
|
|
float_mul(out_val, op1_val, op2_val);
|
|
}
|
|
break;
|
|
case IrBinOpMultWrap:
|
|
assert(type_entry->id == ZigTypeIdInt);
|
|
bigint_mul_wrap(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint,
|
|
type_entry->data.integral.bit_count, type_entry->data.integral.is_signed);
|
|
break;
|
|
case IrBinOpDivUnspecified:
|
|
assert(is_float);
|
|
float_div(out_val, op1_val, op2_val);
|
|
break;
|
|
case IrBinOpDivTrunc:
|
|
if (is_int) {
|
|
bigint_div_trunc(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
|
|
} else {
|
|
float_div_trunc(out_val, op1_val, op2_val);
|
|
}
|
|
break;
|
|
case IrBinOpDivFloor:
|
|
if (is_int) {
|
|
bigint_div_floor(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
|
|
} else {
|
|
float_div_floor(out_val, op1_val, op2_val);
|
|
}
|
|
break;
|
|
case IrBinOpDivExact:
|
|
if (is_int) {
|
|
bigint_div_trunc(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
|
|
BigInt remainder;
|
|
bigint_rem(&remainder, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
|
|
if (bigint_cmp_zero(&remainder) != CmpEQ) {
|
|
return ir_add_error(ira, source_instr, buf_sprintf("exact division had a remainder"));
|
|
}
|
|
} else {
|
|
float_div_trunc(out_val, op1_val, op2_val);
|
|
ConstExprValue remainder = {};
|
|
float_rem(&remainder, op1_val, op2_val);
|
|
if (float_cmp_zero(&remainder) != CmpEQ) {
|
|
return ir_add_error(ira, source_instr, buf_sprintf("exact division had a remainder"));
|
|
}
|
|
}
|
|
break;
|
|
case IrBinOpRemRem:
|
|
if (is_int) {
|
|
bigint_rem(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
|
|
} else {
|
|
float_rem(out_val, op1_val, op2_val);
|
|
}
|
|
break;
|
|
case IrBinOpRemMod:
|
|
if (is_int) {
|
|
bigint_mod(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
|
|
} else {
|
|
float_mod(out_val, op1_val, op2_val);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (type_entry->id == ZigTypeIdInt) {
|
|
if (!bigint_fits_in_bits(&out_val->data.x_bigint, type_entry->data.integral.bit_count,
|
|
type_entry->data.integral.is_signed))
|
|
{
|
|
return ir_add_error(ira, source_instr, buf_sprintf("operation caused overflow"));
|
|
}
|
|
}
|
|
|
|
out_val->type = type_entry;
|
|
out_val->special = ConstValSpecialStatic;
|
|
return nullptr;
|
|
}
|
|
|
|
// This works on operands that have already been checked to be comptime known.
|
|
static IrInstruction *ir_analyze_math_op(IrAnalyze *ira, IrInstruction *source_instr,
|
|
ZigType *type_entry, ConstExprValue *op1_val, IrBinOp op_id, ConstExprValue *op2_val)
|
|
{
|
|
IrInstruction *result_instruction = ir_const(ira, source_instr, type_entry);
|
|
ConstExprValue *out_val = &result_instruction->value;
|
|
if (type_entry->id == ZigTypeIdVector) {
|
|
expand_undef_array(ira->codegen, op1_val);
|
|
expand_undef_array(ira->codegen, op2_val);
|
|
out_val->special = ConstValSpecialUndef;
|
|
expand_undef_array(ira->codegen, out_val);
|
|
size_t len = type_entry->data.vector.len;
|
|
ZigType *scalar_type = type_entry->data.vector.elem_type;
|
|
for (size_t i = 0; i < len; i += 1) {
|
|
ConstExprValue *scalar_op1_val = &op1_val->data.x_array.data.s_none.elements[i];
|
|
ConstExprValue *scalar_op2_val = &op2_val->data.x_array.data.s_none.elements[i];
|
|
ConstExprValue *scalar_out_val = &out_val->data.x_array.data.s_none.elements[i];
|
|
assert(scalar_op1_val->type == scalar_type);
|
|
assert(scalar_op2_val->type == scalar_type);
|
|
assert(scalar_out_val->type == scalar_type);
|
|
ErrorMsg *msg = ir_eval_math_op_scalar(ira, source_instr, scalar_type,
|
|
scalar_op1_val, op_id, scalar_op2_val, scalar_out_val);
|
|
if (msg != nullptr) {
|
|
add_error_note(ira->codegen, msg, source_instr->source_node,
|
|
buf_sprintf("when computing vector element at index %" ZIG_PRI_usize, i));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
out_val->type = type_entry;
|
|
out_val->special = ConstValSpecialStatic;
|
|
} else {
|
|
if (ir_eval_math_op_scalar(ira, source_instr, type_entry, op1_val, op_id, op2_val, out_val) != nullptr) {
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
return ir_implicit_cast(ira, result_instruction, type_entry);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
|
|
IrInstruction *op1 = bin_op_instruction->op1->child;
|
|
if (type_is_invalid(op1->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (op1->value.type->id != ZigTypeIdInt && op1->value.type->id != ZigTypeIdComptimeInt) {
|
|
ir_add_error(ira, &bin_op_instruction->base,
|
|
buf_sprintf("bit shifting operation expected integer type, found '%s'",
|
|
buf_ptr(&op1->value.type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *op2 = bin_op_instruction->op2->child;
|
|
if (type_is_invalid(op2->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *casted_op2;
|
|
IrBinOp op_id = bin_op_instruction->op_id;
|
|
if (op1->value.type->id == ZigTypeIdComptimeInt) {
|
|
casted_op2 = op2;
|
|
|
|
if (op_id == IrBinOpBitShiftLeftLossy) {
|
|
op_id = IrBinOpBitShiftLeftExact;
|
|
}
|
|
|
|
if (casted_op2->value.data.x_bigint.is_negative) {
|
|
Buf *val_buf = buf_alloc();
|
|
bigint_append_buf(val_buf, &casted_op2->value.data.x_bigint, 10);
|
|
ir_add_error(ira, casted_op2, buf_sprintf("shift by negative value %s", buf_ptr(val_buf)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
} else {
|
|
ZigType *shift_amt_type = get_smallest_unsigned_int_type(ira->codegen,
|
|
op1->value.type->data.integral.bit_count - 1);
|
|
if (bin_op_instruction->op_id == IrBinOpBitShiftLeftLossy &&
|
|
op2->value.type->id == ZigTypeIdComptimeInt) {
|
|
if (!bigint_fits_in_bits(&op2->value.data.x_bigint,
|
|
shift_amt_type->data.integral.bit_count,
|
|
op2->value.data.x_bigint.is_negative)) {
|
|
Buf *val_buf = buf_alloc();
|
|
bigint_append_buf(val_buf, &op2->value.data.x_bigint, 10);
|
|
ErrorMsg* msg = ir_add_error(ira,
|
|
&bin_op_instruction->base,
|
|
buf_sprintf("RHS of shift is too large for LHS type"));
|
|
add_error_note(
|
|
ira->codegen,
|
|
msg,
|
|
op2->source_node,
|
|
buf_sprintf("value %s cannot fit into type %s",
|
|
buf_ptr(val_buf),
|
|
buf_ptr(&shift_amt_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
|
|
casted_op2 = ir_implicit_cast(ira, op2, shift_amt_type);
|
|
if (casted_op2 == ira->codegen->invalid_instruction)
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (instr_is_comptime(op1) && instr_is_comptime(casted_op2)) {
|
|
ConstExprValue *op1_val = ir_resolve_const(ira, op1, UndefBad);
|
|
if (op1_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad);
|
|
if (op2_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
return ir_analyze_math_op(ira, &bin_op_instruction->base, op1->value.type, op1_val, op_id, op2_val);
|
|
} else if (op1->value.type->id == ZigTypeIdComptimeInt) {
|
|
ir_add_error(ira, &bin_op_instruction->base,
|
|
buf_sprintf("LHS of shift must be an integer type, or RHS must be compile-time known"));
|
|
return ira->codegen->invalid_instruction;
|
|
} else if (instr_is_comptime(casted_op2) && bigint_cmp_zero(&casted_op2->value.data.x_bigint) == CmpEQ) {
|
|
IrInstruction *result = ir_build_cast(&ira->new_irb, bin_op_instruction->base.scope,
|
|
bin_op_instruction->base.source_node, op1->value.type, op1, CastOpNoop);
|
|
result->value.type = op1->value.type;
|
|
return result;
|
|
}
|
|
|
|
IrInstruction *result = ir_build_bin_op(&ira->new_irb, bin_op_instruction->base.scope,
|
|
bin_op_instruction->base.source_node, op_id,
|
|
op1, casted_op2, bin_op_instruction->safety_check_on);
|
|
result->value.type = op1->value.type;
|
|
return result;
|
|
}
|
|
|
|
static bool ok_float_op(IrBinOp op) {
|
|
switch (op) {
|
|
case IrBinOpInvalid:
|
|
zig_unreachable();
|
|
case IrBinOpAdd:
|
|
case IrBinOpSub:
|
|
case IrBinOpMult:
|
|
case IrBinOpDivUnspecified:
|
|
case IrBinOpDivTrunc:
|
|
case IrBinOpDivFloor:
|
|
case IrBinOpDivExact:
|
|
case IrBinOpRemRem:
|
|
case IrBinOpRemMod:
|
|
return true;
|
|
|
|
case IrBinOpBoolOr:
|
|
case IrBinOpBoolAnd:
|
|
case IrBinOpCmpEq:
|
|
case IrBinOpCmpNotEq:
|
|
case IrBinOpCmpLessThan:
|
|
case IrBinOpCmpGreaterThan:
|
|
case IrBinOpCmpLessOrEq:
|
|
case IrBinOpCmpGreaterOrEq:
|
|
case IrBinOpBinOr:
|
|
case IrBinOpBinXor:
|
|
case IrBinOpBinAnd:
|
|
case IrBinOpBitShiftLeftLossy:
|
|
case IrBinOpBitShiftLeftExact:
|
|
case IrBinOpBitShiftRightLossy:
|
|
case IrBinOpBitShiftRightExact:
|
|
case IrBinOpAddWrap:
|
|
case IrBinOpSubWrap:
|
|
case IrBinOpMultWrap:
|
|
case IrBinOpRemUnspecified:
|
|
case IrBinOpArrayCat:
|
|
case IrBinOpArrayMult:
|
|
case IrBinOpMergeErrorSets:
|
|
return false;
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) {
|
|
if (lhs_type->id != ZigTypeIdPointer)
|
|
return false;
|
|
switch (op) {
|
|
case IrBinOpAdd:
|
|
case IrBinOpSub:
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
switch (lhs_type->data.pointer.ptr_len) {
|
|
case PtrLenSingle:
|
|
return false;
|
|
case PtrLenUnknown:
|
|
case PtrLenC:
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp *instruction) {
|
|
Error err;
|
|
|
|
IrInstruction *op1 = instruction->op1->child;
|
|
if (type_is_invalid(op1->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *op2 = instruction->op2->child;
|
|
if (type_is_invalid(op2->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrBinOp op_id = instruction->op_id;
|
|
|
|
// look for pointer math
|
|
if (is_pointer_arithmetic_allowed(op1->value.type, op_id)) {
|
|
IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, ira->codegen->builtin_types.entry_usize);
|
|
if (type_is_invalid(casted_op2->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (op1->value.special == ConstValSpecialUndef || casted_op2->value.special == ConstValSpecialUndef) {
|
|
IrInstruction *result = ir_const(ira, &instruction->base, op1->value.type);
|
|
result->value.special = ConstValSpecialUndef;
|
|
return result;
|
|
}
|
|
if (casted_op2->value.special == ConstValSpecialStatic && op1->value.special == ConstValSpecialStatic &&
|
|
(op1->value.data.x_ptr.special == ConstPtrSpecialHardCodedAddr ||
|
|
op1->value.data.x_ptr.special == ConstPtrSpecialNull))
|
|
{
|
|
uint64_t start_addr = (op1->value.data.x_ptr.special == ConstPtrSpecialNull) ?
|
|
0 : op1->value.data.x_ptr.data.hard_coded_addr.addr;
|
|
uint64_t elem_offset;
|
|
if (!ir_resolve_usize(ira, casted_op2, &elem_offset))
|
|
return ira->codegen->invalid_instruction;
|
|
ZigType *elem_type = op1->value.type->data.pointer.child_type;
|
|
if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusSizeKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
uint64_t byte_offset = type_size(ira->codegen, elem_type) * elem_offset;
|
|
uint64_t new_addr;
|
|
if (op_id == IrBinOpAdd) {
|
|
new_addr = start_addr + byte_offset;
|
|
} else if (op_id == IrBinOpSub) {
|
|
new_addr = start_addr - byte_offset;
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
IrInstruction *result = ir_const(ira, &instruction->base, op1->value.type);
|
|
result->value.data.x_ptr.special = ConstPtrSpecialHardCodedAddr;
|
|
result->value.data.x_ptr.mut = ConstPtrMutRuntimeVar;
|
|
result->value.data.x_ptr.data.hard_coded_addr.addr = new_addr;
|
|
return result;
|
|
}
|
|
|
|
IrInstruction *result = ir_build_bin_op(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node, op_id, op1, casted_op2, true);
|
|
result->value.type = op1->value.type;
|
|
return result;
|
|
}
|
|
|
|
IrInstruction *instructions[] = {op1, op2};
|
|
ZigType *resolved_type = ir_resolve_peer_types(ira, instruction->base.source_node, nullptr, instructions, 2);
|
|
if (type_is_invalid(resolved_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
bool is_int = resolved_type->id == ZigTypeIdInt || resolved_type->id == ZigTypeIdComptimeInt;
|
|
bool is_float = resolved_type->id == ZigTypeIdFloat || resolved_type->id == ZigTypeIdComptimeFloat;
|
|
bool is_signed_div = (
|
|
(resolved_type->id == ZigTypeIdInt && resolved_type->data.integral.is_signed) ||
|
|
resolved_type->id == ZigTypeIdFloat ||
|
|
(resolved_type->id == ZigTypeIdComptimeFloat &&
|
|
((bigfloat_cmp_zero(&op1->value.data.x_bigfloat) != CmpGT) !=
|
|
(bigfloat_cmp_zero(&op2->value.data.x_bigfloat) != CmpGT))) ||
|
|
(resolved_type->id == ZigTypeIdComptimeInt &&
|
|
((bigint_cmp_zero(&op1->value.data.x_bigint) != CmpGT) !=
|
|
(bigint_cmp_zero(&op2->value.data.x_bigint) != CmpGT)))
|
|
);
|
|
if (op_id == IrBinOpDivUnspecified && is_int) {
|
|
if (is_signed_div) {
|
|
bool ok = false;
|
|
if (instr_is_comptime(op1) && instr_is_comptime(op2)) {
|
|
ConstExprValue *op1_val = ir_resolve_const(ira, op1, UndefBad);
|
|
if (op1_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ConstExprValue *op2_val = ir_resolve_const(ira, op2, UndefBad);
|
|
if (op2_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (bigint_cmp_zero(&op2_val->data.x_bigint) == CmpEQ) {
|
|
// the division by zero error will be caught later, but we don't have a
|
|
// division function ambiguity problem.
|
|
op_id = IrBinOpDivTrunc;
|
|
ok = true;
|
|
} else {
|
|
BigInt trunc_result;
|
|
BigInt floor_result;
|
|
bigint_div_trunc(&trunc_result, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
|
|
bigint_div_floor(&floor_result, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
|
|
if (bigint_cmp(&trunc_result, &floor_result) == CmpEQ) {
|
|
ok = true;
|
|
op_id = IrBinOpDivTrunc;
|
|
}
|
|
}
|
|
}
|
|
if (!ok) {
|
|
ir_add_error(ira, &instruction->base,
|
|
buf_sprintf("division with '%s' and '%s': signed integers must use @divTrunc, @divFloor, or @divExact",
|
|
buf_ptr(&op1->value.type->name),
|
|
buf_ptr(&op2->value.type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
} else {
|
|
op_id = IrBinOpDivTrunc;
|
|
}
|
|
} else if (op_id == IrBinOpRemUnspecified) {
|
|
if (is_signed_div && (is_int || is_float)) {
|
|
bool ok = false;
|
|
if (instr_is_comptime(op1) && instr_is_comptime(op2)) {
|
|
ConstExprValue *op1_val = ir_resolve_const(ira, op1, UndefBad);
|
|
if (op1_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (is_int) {
|
|
ConstExprValue *op2_val = ir_resolve_const(ira, op2, UndefBad);
|
|
if (op2_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (bigint_cmp_zero(&op2->value.data.x_bigint) == CmpEQ) {
|
|
// the division by zero error will be caught later, but we don't
|
|
// have a remainder function ambiguity problem
|
|
ok = true;
|
|
} else {
|
|
BigInt rem_result;
|
|
BigInt mod_result;
|
|
bigint_rem(&rem_result, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
|
|
bigint_mod(&mod_result, &op1_val->data.x_bigint, &op2_val->data.x_bigint);
|
|
ok = bigint_cmp(&rem_result, &mod_result) == CmpEQ;
|
|
}
|
|
} else {
|
|
IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, resolved_type);
|
|
if (casted_op2 == ira->codegen->invalid_instruction)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad);
|
|
if (op2_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (float_cmp_zero(&casted_op2->value) == CmpEQ) {
|
|
// the division by zero error will be caught later, but we don't
|
|
// have a remainder function ambiguity problem
|
|
ok = true;
|
|
} else {
|
|
ConstExprValue rem_result = {};
|
|
ConstExprValue mod_result = {};
|
|
float_rem(&rem_result, op1_val, op2_val);
|
|
float_mod(&mod_result, op1_val, op2_val);
|
|
ok = float_cmp(&rem_result, &mod_result) == CmpEQ;
|
|
}
|
|
}
|
|
}
|
|
if (!ok) {
|
|
ir_add_error(ira, &instruction->base,
|
|
buf_sprintf("remainder division with '%s' and '%s': signed integers and floats must use @rem or @mod",
|
|
buf_ptr(&op1->value.type->name),
|
|
buf_ptr(&op2->value.type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
op_id = IrBinOpRemRem;
|
|
}
|
|
|
|
bool ok = false;
|
|
if (is_int) {
|
|
ok = true;
|
|
} else if (is_float && ok_float_op(op_id)) {
|
|
ok = true;
|
|
} else if (resolved_type->id == ZigTypeIdVector) {
|
|
ZigType *elem_type = resolved_type->data.vector.elem_type;
|
|
if (elem_type->id == ZigTypeIdInt || elem_type->id == ZigTypeIdComptimeInt) {
|
|
ok = true;
|
|
} else if ((elem_type->id == ZigTypeIdFloat || elem_type->id == ZigTypeIdComptimeFloat) && ok_float_op(op_id)) {
|
|
ok = true;
|
|
}
|
|
}
|
|
if (!ok) {
|
|
AstNode *source_node = instruction->base.source_node;
|
|
ir_add_error_node(ira, source_node,
|
|
buf_sprintf("invalid operands to binary expression: '%s' and '%s'",
|
|
buf_ptr(&op1->value.type->name),
|
|
buf_ptr(&op2->value.type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (resolved_type->id == ZigTypeIdComptimeInt) {
|
|
if (op_id == IrBinOpAddWrap) {
|
|
op_id = IrBinOpAdd;
|
|
} else if (op_id == IrBinOpSubWrap) {
|
|
op_id = IrBinOpSub;
|
|
} else if (op_id == IrBinOpMultWrap) {
|
|
op_id = IrBinOpMult;
|
|
}
|
|
}
|
|
|
|
IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, resolved_type);
|
|
if (casted_op1 == ira->codegen->invalid_instruction)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, resolved_type);
|
|
if (casted_op2 == ira->codegen->invalid_instruction)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2)) {
|
|
ConstExprValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad);
|
|
if (op1_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
ConstExprValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad);
|
|
if (op2_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
return ir_analyze_math_op(ira, &instruction->base, resolved_type, op1_val, op_id, op2_val);
|
|
}
|
|
|
|
IrInstruction *result = ir_build_bin_op(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node, op_id, casted_op1, casted_op2, instruction->safety_check_on);
|
|
result->value.type = resolved_type;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *instruction) {
|
|
IrInstruction *op1 = instruction->op1->child;
|
|
ZigType *op1_type = op1->value.type;
|
|
if (type_is_invalid(op1_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *op2 = instruction->op2->child;
|
|
ZigType *op2_type = op2->value.type;
|
|
if (type_is_invalid(op2_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ConstExprValue *op1_val = ir_resolve_const(ira, op1, UndefBad);
|
|
if (!op1_val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ConstExprValue *op2_val = ir_resolve_const(ira, op2, UndefBad);
|
|
if (!op2_val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ConstExprValue *op1_array_val;
|
|
size_t op1_array_index;
|
|
size_t op1_array_end;
|
|
ZigType *child_type;
|
|
if (op1_type->id == ZigTypeIdArray) {
|
|
child_type = op1_type->data.array.child_type;
|
|
op1_array_val = op1_val;
|
|
op1_array_index = 0;
|
|
op1_array_end = op1_type->data.array.len;
|
|
} else if (op1_type->id == ZigTypeIdPointer &&
|
|
op1_type->data.pointer.child_type == ira->codegen->builtin_types.entry_u8 &&
|
|
op1_val->data.x_ptr.special == ConstPtrSpecialBaseArray &&
|
|
op1_val->data.x_ptr.data.base_array.is_cstr)
|
|
{
|
|
child_type = op1_type->data.pointer.child_type;
|
|
op1_array_val = op1_val->data.x_ptr.data.base_array.array_val;
|
|
op1_array_index = op1_val->data.x_ptr.data.base_array.elem_index;
|
|
op1_array_end = op1_array_val->type->data.array.len - 1;
|
|
} else if (is_slice(op1_type)) {
|
|
ZigType *ptr_type = op1_type->data.structure.fields[slice_ptr_index].type_entry;
|
|
child_type = ptr_type->data.pointer.child_type;
|
|
ConstExprValue *ptr_val = &op1_val->data.x_struct.fields[slice_ptr_index];
|
|
assert(ptr_val->data.x_ptr.special == ConstPtrSpecialBaseArray);
|
|
op1_array_val = ptr_val->data.x_ptr.data.base_array.array_val;
|
|
op1_array_index = ptr_val->data.x_ptr.data.base_array.elem_index;
|
|
ConstExprValue *len_val = &op1_val->data.x_struct.fields[slice_len_index];
|
|
op1_array_end = op1_array_index + bigint_as_unsigned(&len_val->data.x_bigint);
|
|
} else {
|
|
ir_add_error(ira, op1,
|
|
buf_sprintf("expected array or C string literal, found '%s'", buf_ptr(&op1->value.type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
ConstExprValue *op2_array_val;
|
|
size_t op2_array_index;
|
|
size_t op2_array_end;
|
|
bool op2_type_valid;
|
|
if (op2_type->id == ZigTypeIdArray) {
|
|
op2_type_valid = op2_type->data.array.child_type == child_type;
|
|
op2_array_val = op2_val;
|
|
op2_array_index = 0;
|
|
op2_array_end = op2_array_val->type->data.array.len;
|
|
} else if (op2_type->id == ZigTypeIdPointer &&
|
|
op2_type->data.pointer.child_type == ira->codegen->builtin_types.entry_u8 &&
|
|
op2_val->data.x_ptr.special == ConstPtrSpecialBaseArray &&
|
|
op2_val->data.x_ptr.data.base_array.is_cstr)
|
|
{
|
|
op2_type_valid = child_type == ira->codegen->builtin_types.entry_u8;
|
|
op2_array_val = op2_val->data.x_ptr.data.base_array.array_val;
|
|
op2_array_index = op2_val->data.x_ptr.data.base_array.elem_index;
|
|
op2_array_end = op2_array_val->type->data.array.len - 1;
|
|
} else if (is_slice(op2_type)) {
|
|
ZigType *ptr_type = op2_type->data.structure.fields[slice_ptr_index].type_entry;
|
|
op2_type_valid = ptr_type->data.pointer.child_type == child_type;
|
|
ConstExprValue *ptr_val = &op2_val->data.x_struct.fields[slice_ptr_index];
|
|
assert(ptr_val->data.x_ptr.special == ConstPtrSpecialBaseArray);
|
|
op2_array_val = ptr_val->data.x_ptr.data.base_array.array_val;
|
|
op2_array_index = ptr_val->data.x_ptr.data.base_array.elem_index;
|
|
ConstExprValue *len_val = &op2_val->data.x_struct.fields[slice_len_index];
|
|
op2_array_end = op2_array_index + bigint_as_unsigned(&len_val->data.x_bigint);
|
|
} else {
|
|
ir_add_error(ira, op2,
|
|
buf_sprintf("expected array or C string literal, found '%s'", buf_ptr(&op2->value.type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
if (!op2_type_valid) {
|
|
ir_add_error(ira, op2, buf_sprintf("expected array of type '%s', found '%s'",
|
|
buf_ptr(&child_type->name),
|
|
buf_ptr(&op2->value.type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
// The type of result is populated in the following if blocks
|
|
IrInstruction *result = ir_const(ira, &instruction->base, nullptr);
|
|
ConstExprValue *out_val = &result->value;
|
|
|
|
ConstExprValue *out_array_val;
|
|
size_t new_len = (op1_array_end - op1_array_index) + (op2_array_end - op2_array_index);
|
|
if (op1_type->id == ZigTypeIdArray || op2_type->id == ZigTypeIdArray) {
|
|
result->value.type = get_array_type(ira->codegen, child_type, new_len);
|
|
|
|
out_array_val = out_val;
|
|
} else if (is_slice(op1_type) || is_slice(op2_type)) {
|
|
ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, child_type,
|
|
true, false, PtrLenUnknown, 0, 0, 0, false);
|
|
result->value.type = get_slice_type(ira->codegen, ptr_type);
|
|
out_array_val = create_const_vals(1);
|
|
out_array_val->special = ConstValSpecialStatic;
|
|
out_array_val->type = get_array_type(ira->codegen, child_type, new_len);
|
|
|
|
out_val->data.x_struct.fields = create_const_vals(2);
|
|
|
|
out_val->data.x_struct.fields[slice_ptr_index].type = ptr_type;
|
|
out_val->data.x_struct.fields[slice_ptr_index].special = ConstValSpecialStatic;
|
|
out_val->data.x_struct.fields[slice_ptr_index].data.x_ptr.special = ConstPtrSpecialBaseArray;
|
|
out_val->data.x_struct.fields[slice_ptr_index].data.x_ptr.data.base_array.array_val = out_array_val;
|
|
out_val->data.x_struct.fields[slice_ptr_index].data.x_ptr.data.base_array.elem_index = 0;
|
|
|
|
out_val->data.x_struct.fields[slice_len_index].type = ira->codegen->builtin_types.entry_usize;
|
|
out_val->data.x_struct.fields[slice_len_index].special = ConstValSpecialStatic;
|
|
bigint_init_unsigned(&out_val->data.x_struct.fields[slice_len_index].data.x_bigint, new_len);
|
|
} else {
|
|
new_len += 1; // null byte
|
|
|
|
// TODO make this `[*]null T` instead of `[*]T`
|
|
result->value.type = get_pointer_to_type_extra(ira->codegen, child_type, true, false, PtrLenUnknown, 0, 0, 0, false);
|
|
|
|
out_array_val = create_const_vals(1);
|
|
out_array_val->special = ConstValSpecialStatic;
|
|
out_array_val->type = get_array_type(ira->codegen, child_type, new_len);
|
|
out_val->data.x_ptr.special = ConstPtrSpecialBaseArray;
|
|
out_val->data.x_ptr.data.base_array.is_cstr = true;
|
|
out_val->data.x_ptr.data.base_array.array_val = out_array_val;
|
|
out_val->data.x_ptr.data.base_array.elem_index = 0;
|
|
}
|
|
|
|
if (op1_array_val->data.x_array.special == ConstArraySpecialUndef &&
|
|
op2_array_val->data.x_array.special == ConstArraySpecialUndef) {
|
|
out_array_val->data.x_array.special = ConstArraySpecialUndef;
|
|
return result;
|
|
}
|
|
|
|
out_array_val->data.x_array.data.s_none.elements = create_const_vals(new_len);
|
|
// TODO handle the buf case here for an optimization
|
|
expand_undef_array(ira->codegen, op1_array_val);
|
|
expand_undef_array(ira->codegen, op2_array_val);
|
|
|
|
size_t next_index = 0;
|
|
for (size_t i = op1_array_index; i < op1_array_end; i += 1, next_index += 1) {
|
|
copy_const_val(&out_array_val->data.x_array.data.s_none.elements[next_index],
|
|
&op1_array_val->data.x_array.data.s_none.elements[i], true);
|
|
}
|
|
for (size_t i = op2_array_index; i < op2_array_end; i += 1, next_index += 1) {
|
|
copy_const_val(&out_array_val->data.x_array.data.s_none.elements[next_index],
|
|
&op2_array_val->data.x_array.data.s_none.elements[i], true);
|
|
}
|
|
if (next_index < new_len) {
|
|
ConstExprValue *null_byte = &out_array_val->data.x_array.data.s_none.elements[next_index];
|
|
init_const_unsigned_negative(null_byte, child_type, 0, false);
|
|
next_index += 1;
|
|
}
|
|
assert(next_index == new_len);
|
|
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp *instruction) {
|
|
IrInstruction *op1 = instruction->op1->child;
|
|
if (type_is_invalid(op1->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *op2 = instruction->op2->child;
|
|
if (type_is_invalid(op2->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ConstExprValue *array_val = ir_resolve_const(ira, op1, UndefBad);
|
|
if (!array_val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
uint64_t mult_amt;
|
|
if (!ir_resolve_usize(ira, op2, &mult_amt))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *array_type = op1->value.type;
|
|
if (array_type->id != ZigTypeIdArray) {
|
|
ir_add_error(ira, op1, buf_sprintf("expected array type, found '%s'", buf_ptr(&op1->value.type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
uint64_t old_array_len = array_type->data.array.len;
|
|
uint64_t new_array_len;
|
|
|
|
if (mul_u64_overflow(old_array_len, mult_amt, &new_array_len))
|
|
{
|
|
ir_add_error(ira, &instruction->base, buf_sprintf("operation results in overflow"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
ZigType *child_type = array_type->data.array.child_type;
|
|
|
|
IrInstruction *result = ir_const(ira, &instruction->base,
|
|
get_array_type(ira->codegen, child_type, new_array_len));
|
|
ConstExprValue *out_val = &result->value;
|
|
if (array_val->data.x_array.special == ConstArraySpecialUndef) {
|
|
out_val->data.x_array.special = ConstArraySpecialUndef;
|
|
return result;
|
|
}
|
|
|
|
// TODO optimize the buf case
|
|
expand_undef_array(ira->codegen, array_val);
|
|
out_val->data.x_array.data.s_none.elements = create_const_vals(new_array_len);
|
|
|
|
uint64_t i = 0;
|
|
for (uint64_t x = 0; x < mult_amt; x += 1) {
|
|
for (uint64_t y = 0; y < old_array_len; y += 1) {
|
|
copy_const_val(&out_val->data.x_array.data.s_none.elements[i],
|
|
&array_val->data.x_array.data.s_none.elements[y], false);
|
|
i += 1;
|
|
}
|
|
}
|
|
assert(i == new_array_len);
|
|
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_merge_error_sets(IrAnalyze *ira, IrInstructionBinOp *instruction) {
|
|
ZigType *op1_type = ir_resolve_error_set_type(ira, &instruction->base, instruction->op1->child);
|
|
if (type_is_invalid(op1_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *op2_type = ir_resolve_error_set_type(ira, &instruction->base, instruction->op2->child);
|
|
if (type_is_invalid(op2_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (type_is_global_error_set(op1_type) ||
|
|
type_is_global_error_set(op2_type))
|
|
{
|
|
return ir_const_type(ira, &instruction->base, ira->codegen->builtin_types.entry_global_error_set);
|
|
}
|
|
|
|
if (!resolve_inferred_error_set(ira->codegen, op1_type, instruction->op1->child->source_node)) {
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (!resolve_inferred_error_set(ira->codegen, op2_type, instruction->op2->child->source_node)) {
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
ErrorTableEntry **errors = allocate<ErrorTableEntry *>(ira->codegen->errors_by_index.length);
|
|
for (uint32_t i = 0, count = op1_type->data.error_set.err_count; i < count; i += 1) {
|
|
ErrorTableEntry *error_entry = op1_type->data.error_set.errors[i];
|
|
assert(errors[error_entry->value] == nullptr);
|
|
errors[error_entry->value] = error_entry;
|
|
}
|
|
ZigType *result_type = get_error_set_union(ira->codegen, errors, op1_type, op2_type);
|
|
free(errors);
|
|
|
|
return ir_const_type(ira, &instruction->base, result_type);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
|
|
IrBinOp op_id = bin_op_instruction->op_id;
|
|
switch (op_id) {
|
|
case IrBinOpInvalid:
|
|
zig_unreachable();
|
|
case IrBinOpBoolOr:
|
|
case IrBinOpBoolAnd:
|
|
return ir_analyze_bin_op_bool(ira, bin_op_instruction);
|
|
case IrBinOpCmpEq:
|
|
case IrBinOpCmpNotEq:
|
|
case IrBinOpCmpLessThan:
|
|
case IrBinOpCmpGreaterThan:
|
|
case IrBinOpCmpLessOrEq:
|
|
case IrBinOpCmpGreaterOrEq:
|
|
return ir_analyze_bin_op_cmp(ira, bin_op_instruction);
|
|
case IrBinOpBitShiftLeftLossy:
|
|
case IrBinOpBitShiftLeftExact:
|
|
case IrBinOpBitShiftRightLossy:
|
|
case IrBinOpBitShiftRightExact:
|
|
return ir_analyze_bit_shift(ira, bin_op_instruction);
|
|
case IrBinOpBinOr:
|
|
case IrBinOpBinXor:
|
|
case IrBinOpBinAnd:
|
|
case IrBinOpAdd:
|
|
case IrBinOpAddWrap:
|
|
case IrBinOpSub:
|
|
case IrBinOpSubWrap:
|
|
case IrBinOpMult:
|
|
case IrBinOpMultWrap:
|
|
case IrBinOpDivUnspecified:
|
|
case IrBinOpDivTrunc:
|
|
case IrBinOpDivFloor:
|
|
case IrBinOpDivExact:
|
|
case IrBinOpRemUnspecified:
|
|
case IrBinOpRemRem:
|
|
case IrBinOpRemMod:
|
|
return ir_analyze_bin_op_math(ira, bin_op_instruction);
|
|
case IrBinOpArrayCat:
|
|
return ir_analyze_array_cat(ira, bin_op_instruction);
|
|
case IrBinOpArrayMult:
|
|
return ir_analyze_array_mult(ira, bin_op_instruction);
|
|
case IrBinOpMergeErrorSets:
|
|
return ir_analyze_merge_error_sets(ira, bin_op_instruction);
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira,
|
|
IrInstructionDeclVarSrc *decl_var_instruction)
|
|
{
|
|
Error err;
|
|
ZigVar *var = decl_var_instruction->var;
|
|
|
|
ZigType *explicit_type = nullptr;
|
|
IrInstruction *var_type = nullptr;
|
|
if (decl_var_instruction->var_type != nullptr) {
|
|
var_type = decl_var_instruction->var_type->child;
|
|
ZigType *proposed_type = ir_resolve_type(ira, var_type);
|
|
explicit_type = validate_var_type(ira->codegen, var_type->source_node, proposed_type);
|
|
if (type_is_invalid(explicit_type)) {
|
|
var->var_type = ira->codegen->builtin_types.entry_invalid;
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
|
|
AstNode *source_node = decl_var_instruction->base.source_node;
|
|
|
|
bool is_comptime_var = ir_get_var_is_comptime(var);
|
|
|
|
bool var_class_requires_const = false;
|
|
|
|
IrInstruction *var_ptr = decl_var_instruction->ptr->child;
|
|
// if this is null, a compiler error happened and did not initialize the variable.
|
|
// if there are no compile errors there may be a missing ir_expr_wrap in pass1 IR generation.
|
|
if (var_ptr == nullptr || type_is_invalid(var_ptr->value.type)) {
|
|
ir_assert(var_ptr != nullptr || ira->codegen->errors.length != 0, &decl_var_instruction->base);
|
|
var->var_type = ira->codegen->builtin_types.entry_invalid;
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
// The ir_build_var_decl_src call is supposed to pass a pointer to the allocation, not an initialization value.
|
|
ir_assert(var_ptr->value.type->id == ZigTypeIdPointer, &decl_var_instruction->base);
|
|
|
|
ZigType *result_type = var_ptr->value.type->data.pointer.child_type;
|
|
if (type_is_invalid(result_type)) {
|
|
result_type = ira->codegen->builtin_types.entry_invalid;
|
|
} else if (result_type->id == ZigTypeIdUnreachable || result_type->id == ZigTypeIdOpaque) {
|
|
zig_unreachable();
|
|
}
|
|
|
|
ConstExprValue *init_val = nullptr;
|
|
if (instr_is_comptime(var_ptr) && var_ptr->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) {
|
|
init_val = const_ptr_pointee(ira, ira->codegen, &var_ptr->value, decl_var_instruction->base.source_node);
|
|
if (is_comptime_var) {
|
|
if (var->gen_is_const) {
|
|
var->const_value = init_val;
|
|
} else {
|
|
var->const_value = create_const_vals(1);
|
|
copy_const_val(var->const_value, init_val, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (type_requires_comptime(ira->codegen, result_type)) {
|
|
case ReqCompTimeInvalid:
|
|
result_type = ira->codegen->builtin_types.entry_invalid;
|
|
break;
|
|
case ReqCompTimeYes:
|
|
var_class_requires_const = true;
|
|
if (!var->gen_is_const && !is_comptime_var) {
|
|
ir_add_error_node(ira, source_node,
|
|
buf_sprintf("variable of type '%s' must be const or comptime",
|
|
buf_ptr(&result_type->name)));
|
|
result_type = ira->codegen->builtin_types.entry_invalid;
|
|
}
|
|
break;
|
|
case ReqCompTimeNo:
|
|
if (init_val != nullptr) {
|
|
if (init_val->special == ConstValSpecialStatic &&
|
|
init_val->type->id == ZigTypeIdFn &&
|
|
init_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr &&
|
|
init_val->data.x_ptr.data.fn.fn_entry->fn_inline == FnInlineAlways)
|
|
{
|
|
var_class_requires_const = true;
|
|
if (!var->src_is_const && !is_comptime_var) {
|
|
ErrorMsg *msg = ir_add_error_node(ira, source_node,
|
|
buf_sprintf("functions marked inline must be stored in const or comptime var"));
|
|
AstNode *proto_node = init_val->data.x_ptr.data.fn.fn_entry->proto_node;
|
|
add_error_note(ira->codegen, msg, proto_node, buf_sprintf("declared here"));
|
|
result_type = ira->codegen->builtin_types.entry_invalid;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (var->var_type != nullptr && !is_comptime_var) {
|
|
// This is at least the second time we've seen this variable declaration during analysis.
|
|
// This means that this is actually a different variable due to, e.g. an inline while loop.
|
|
// We make a new variable so that it can hold a different type, and so the debug info can
|
|
// be distinct.
|
|
ZigVar *new_var = create_local_var(ira->codegen, var->decl_node, var->child_scope,
|
|
&var->name, var->src_is_const, var->gen_is_const, var->shadowable, var->is_comptime, true);
|
|
new_var->owner_exec = var->owner_exec;
|
|
new_var->align_bytes = var->align_bytes;
|
|
if (var->mem_slot_index != SIZE_MAX) {
|
|
ConstExprValue *vals = create_const_vals(1);
|
|
new_var->mem_slot_index = ira->exec_context.mem_slot_list.length;
|
|
ira->exec_context.mem_slot_list.append(vals);
|
|
}
|
|
|
|
var->next_var = new_var;
|
|
var = new_var;
|
|
}
|
|
|
|
// This must be done after possibly creating a new variable above
|
|
var->ref_count = 0;
|
|
|
|
var->var_type = result_type;
|
|
assert(var->var_type);
|
|
|
|
if (type_is_invalid(result_type)) {
|
|
return ir_const_void(ira, &decl_var_instruction->base);
|
|
}
|
|
|
|
if (decl_var_instruction->align_value == nullptr) {
|
|
if ((err = type_resolve(ira->codegen, result_type, ResolveStatusAlignmentKnown))) {
|
|
var->var_type = ira->codegen->builtin_types.entry_invalid;
|
|
return ir_const_void(ira, &decl_var_instruction->base);
|
|
}
|
|
var->align_bytes = get_abi_alignment(ira->codegen, result_type);
|
|
} else {
|
|
if (!ir_resolve_align(ira, decl_var_instruction->align_value->child, &var->align_bytes)) {
|
|
var->var_type = ira->codegen->builtin_types.entry_invalid;
|
|
}
|
|
}
|
|
|
|
if (init_val != nullptr && init_val->special != ConstValSpecialRuntime) {
|
|
// Resolve ConstPtrMutInfer
|
|
if (var->gen_is_const) {
|
|
var_ptr->value.data.x_ptr.mut = ConstPtrMutComptimeConst;
|
|
} else if (is_comptime_var) {
|
|
var_ptr->value.data.x_ptr.mut = ConstPtrMutComptimeVar;
|
|
} else {
|
|
// we need a runtime ptr but we have a comptime val.
|
|
// since it's a comptime val there are no instructions for it.
|
|
// we memcpy the init value here
|
|
IrInstruction *deref = ir_get_deref(ira, var_ptr, var_ptr, nullptr);
|
|
// If this assertion trips, something is wrong with the IR instructions, because
|
|
// we expected the above deref to return a constant value, but it created a runtime
|
|
// instruction.
|
|
assert(deref->value.special != ConstValSpecialRuntime);
|
|
var_ptr->value.special = ConstValSpecialRuntime;
|
|
ir_analyze_store_ptr(ira, var_ptr, var_ptr, deref, false);
|
|
}
|
|
|
|
if (var_ptr->value.special == ConstValSpecialStatic && var->mem_slot_index != SIZE_MAX) {
|
|
assert(var->mem_slot_index < ira->exec_context.mem_slot_list.length);
|
|
ConstExprValue *mem_slot = ira->exec_context.mem_slot_list.at(var->mem_slot_index);
|
|
copy_const_val(mem_slot, init_val, !is_comptime_var || var->gen_is_const);
|
|
|
|
if (is_comptime_var || (var_class_requires_const && var->gen_is_const)) {
|
|
return ir_const_void(ira, &decl_var_instruction->base);
|
|
}
|
|
}
|
|
} else if (is_comptime_var) {
|
|
ir_add_error(ira, &decl_var_instruction->base,
|
|
buf_sprintf("cannot store runtime value in compile time variable"));
|
|
var->var_type = ira->codegen->builtin_types.entry_invalid;
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec);
|
|
if (fn_entry)
|
|
fn_entry->variable_list.append(var);
|
|
|
|
return ir_build_var_decl_gen(ira, &decl_var_instruction->base, var, var_ptr);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructionExport *instruction) {
|
|
IrInstruction *name = instruction->name->child;
|
|
Buf *symbol_name = ir_resolve_str(ira, name);
|
|
if (symbol_name == nullptr) {
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *target = instruction->target->child;
|
|
if (type_is_invalid(target->value.type)) {
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
GlobalLinkageId global_linkage_id = GlobalLinkageIdStrong;
|
|
if (instruction->linkage != nullptr) {
|
|
IrInstruction *linkage_value = instruction->linkage->child;
|
|
if (!ir_resolve_global_linkage(ira, linkage_value, &global_linkage_id)) {
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
|
|
// TODO: This function needs to be audited.
|
|
// It's not clear how all the different types are supposed to be handled.
|
|
// Need comprehensive tests for exporting one thing in one file and declaring an extern var
|
|
// in another file.
|
|
TldFn *tld_fn = allocate<TldFn>(1);
|
|
tld_fn->base.id = TldIdFn;
|
|
tld_fn->base.source_node = instruction->base.source_node;
|
|
|
|
auto entry = ira->codegen->exported_symbol_names.put_unique(symbol_name, &tld_fn->base);
|
|
if (entry) {
|
|
AstNode *other_export_node = entry->value->source_node;
|
|
ErrorMsg *msg = ir_add_error(ira, &instruction->base,
|
|
buf_sprintf("exported symbol collision: '%s'", buf_ptr(symbol_name)));
|
|
add_error_note(ira->codegen, msg, other_export_node, buf_sprintf("other symbol is here"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
bool want_var_export = false;
|
|
switch (target->value.type->id) {
|
|
case ZigTypeIdInvalid:
|
|
case ZigTypeIdUnreachable:
|
|
zig_unreachable();
|
|
case ZigTypeIdFn: {
|
|
assert(target->value.data.x_ptr.special == ConstPtrSpecialFunction);
|
|
ZigFn *fn_entry = target->value.data.x_ptr.data.fn.fn_entry;
|
|
tld_fn->fn_entry = fn_entry;
|
|
CallingConvention cc = fn_entry->type_entry->data.fn.fn_type_id.cc;
|
|
switch (cc) {
|
|
case CallingConventionUnspecified: {
|
|
ErrorMsg *msg = ir_add_error(ira, target,
|
|
buf_sprintf("exported function must specify calling convention"));
|
|
add_error_note(ira->codegen, msg, fn_entry->proto_node, buf_sprintf("declared here"));
|
|
} break;
|
|
case CallingConventionAsync: {
|
|
ErrorMsg *msg = ir_add_error(ira, target,
|
|
buf_sprintf("exported function cannot be async"));
|
|
add_error_note(ira->codegen, msg, fn_entry->proto_node, buf_sprintf("declared here"));
|
|
} break;
|
|
case CallingConventionC:
|
|
case CallingConventionNaked:
|
|
case CallingConventionCold:
|
|
case CallingConventionStdcall:
|
|
add_fn_export(ira->codegen, fn_entry, symbol_name, global_linkage_id, cc == CallingConventionC);
|
|
break;
|
|
}
|
|
} break;
|
|
case ZigTypeIdStruct:
|
|
if (is_slice(target->value.type)) {
|
|
ir_add_error(ira, target,
|
|
buf_sprintf("unable to export value of type '%s'", buf_ptr(&target->value.type->name)));
|
|
} else if (target->value.type->data.structure.layout != ContainerLayoutExtern) {
|
|
ErrorMsg *msg = ir_add_error(ira, target,
|
|
buf_sprintf("exported struct value must be declared extern"));
|
|
add_error_note(ira->codegen, msg, target->value.type->data.structure.decl_node, buf_sprintf("declared here"));
|
|
} else {
|
|
want_var_export = true;
|
|
}
|
|
break;
|
|
case ZigTypeIdUnion:
|
|
if (target->value.type->data.unionation.layout != ContainerLayoutExtern) {
|
|
ErrorMsg *msg = ir_add_error(ira, target,
|
|
buf_sprintf("exported union value must be declared extern"));
|
|
add_error_note(ira->codegen, msg, target->value.type->data.unionation.decl_node, buf_sprintf("declared here"));
|
|
} else {
|
|
want_var_export = true;
|
|
}
|
|
break;
|
|
case ZigTypeIdEnum:
|
|
if (target->value.type->data.enumeration.layout != ContainerLayoutExtern) {
|
|
ErrorMsg *msg = ir_add_error(ira, target,
|
|
buf_sprintf("exported enum value must be declared extern"));
|
|
add_error_note(ira->codegen, msg, target->value.type->data.enumeration.decl_node, buf_sprintf("declared here"));
|
|
} else {
|
|
want_var_export = true;
|
|
}
|
|
break;
|
|
case ZigTypeIdArray:
|
|
if (!type_allowed_in_extern(ira->codegen, target->value.type->data.array.child_type)) {
|
|
ir_add_error(ira, target,
|
|
buf_sprintf("array element type '%s' not extern-compatible",
|
|
buf_ptr(&target->value.type->data.array.child_type->name)));
|
|
} else {
|
|
want_var_export = true;
|
|
}
|
|
break;
|
|
case ZigTypeIdMetaType: {
|
|
ZigType *type_value = target->value.data.x_type;
|
|
switch (type_value->id) {
|
|
case ZigTypeIdInvalid:
|
|
zig_unreachable();
|
|
case ZigTypeIdStruct:
|
|
if (is_slice(type_value)) {
|
|
ir_add_error(ira, target,
|
|
buf_sprintf("unable to export type '%s'", buf_ptr(&type_value->name)));
|
|
} else if (type_value->data.structure.layout != ContainerLayoutExtern) {
|
|
ErrorMsg *msg = ir_add_error(ira, target,
|
|
buf_sprintf("exported struct must be declared extern"));
|
|
add_error_note(ira->codegen, msg, type_value->data.structure.decl_node, buf_sprintf("declared here"));
|
|
}
|
|
break;
|
|
case ZigTypeIdUnion:
|
|
if (type_value->data.unionation.layout != ContainerLayoutExtern) {
|
|
ErrorMsg *msg = ir_add_error(ira, target,
|
|
buf_sprintf("exported union must be declared extern"));
|
|
add_error_note(ira->codegen, msg, type_value->data.unionation.decl_node, buf_sprintf("declared here"));
|
|
}
|
|
break;
|
|
case ZigTypeIdEnum:
|
|
if (type_value->data.enumeration.layout != ContainerLayoutExtern) {
|
|
ErrorMsg *msg = ir_add_error(ira, target,
|
|
buf_sprintf("exported enum must be declared extern"));
|
|
add_error_note(ira->codegen, msg, type_value->data.enumeration.decl_node, buf_sprintf("declared here"));
|
|
}
|
|
break;
|
|
case ZigTypeIdFn: {
|
|
if (type_value->data.fn.fn_type_id.cc == CallingConventionUnspecified) {
|
|
ir_add_error(ira, target,
|
|
buf_sprintf("exported function type must specify calling convention"));
|
|
}
|
|
} break;
|
|
case ZigTypeIdInt:
|
|
case ZigTypeIdFloat:
|
|
case ZigTypeIdPointer:
|
|
case ZigTypeIdArray:
|
|
case ZigTypeIdBool:
|
|
case ZigTypeIdVector:
|
|
break;
|
|
case ZigTypeIdMetaType:
|
|
case ZigTypeIdVoid:
|
|
case ZigTypeIdUnreachable:
|
|
case ZigTypeIdComptimeFloat:
|
|
case ZigTypeIdComptimeInt:
|
|
case ZigTypeIdEnumLiteral:
|
|
case ZigTypeIdUndefined:
|
|
case ZigTypeIdNull:
|
|
case ZigTypeIdOptional:
|
|
case ZigTypeIdErrorUnion:
|
|
case ZigTypeIdErrorSet:
|
|
case ZigTypeIdBoundFn:
|
|
case ZigTypeIdArgTuple:
|
|
case ZigTypeIdOpaque:
|
|
case ZigTypeIdCoroFrame:
|
|
case ZigTypeIdAnyFrame:
|
|
ir_add_error(ira, target,
|
|
buf_sprintf("invalid export target '%s'", buf_ptr(&type_value->name)));
|
|
break;
|
|
}
|
|
} break;
|
|
case ZigTypeIdInt:
|
|
break;
|
|
case ZigTypeIdVoid:
|
|
case ZigTypeIdBool:
|
|
case ZigTypeIdFloat:
|
|
case ZigTypeIdPointer:
|
|
case ZigTypeIdComptimeFloat:
|
|
case ZigTypeIdComptimeInt:
|
|
case ZigTypeIdUndefined:
|
|
case ZigTypeIdNull:
|
|
case ZigTypeIdOptional:
|
|
case ZigTypeIdErrorUnion:
|
|
case ZigTypeIdErrorSet:
|
|
case ZigTypeIdVector:
|
|
zig_panic("TODO export const value of type %s", buf_ptr(&target->value.type->name));
|
|
case ZigTypeIdBoundFn:
|
|
case ZigTypeIdArgTuple:
|
|
case ZigTypeIdOpaque:
|
|
case ZigTypeIdEnumLiteral:
|
|
case ZigTypeIdCoroFrame:
|
|
case ZigTypeIdAnyFrame:
|
|
ir_add_error(ira, target,
|
|
buf_sprintf("invalid export target type '%s'", buf_ptr(&target->value.type->name)));
|
|
break;
|
|
}
|
|
|
|
// TODO audit the various ways to use @export
|
|
if (want_var_export && target->id == IrInstructionIdLoadPtrGen) {
|
|
IrInstructionLoadPtrGen *load_ptr = reinterpret_cast<IrInstructionLoadPtrGen *>(target);
|
|
if (load_ptr->ptr->id == IrInstructionIdVarPtr) {
|
|
IrInstructionVarPtr *var_ptr = reinterpret_cast<IrInstructionVarPtr *>(load_ptr->ptr);
|
|
ZigVar *var = var_ptr->var;
|
|
add_var_export(ira->codegen, var, symbol_name, global_linkage_id);
|
|
}
|
|
}
|
|
|
|
return ir_const_void(ira, &instruction->base);
|
|
}
|
|
|
|
static bool exec_has_err_ret_trace(CodeGen *g, IrExecutable *exec) {
|
|
ZigFn *fn_entry = exec_fn_entry(exec);
|
|
return fn_entry != nullptr && fn_entry->calls_or_awaits_errorable_fn && g->have_err_ret_tracing;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_error_return_trace(IrAnalyze *ira,
|
|
IrInstructionErrorReturnTrace *instruction)
|
|
{
|
|
if (instruction->optional == IrInstructionErrorReturnTrace::Null) {
|
|
ZigType *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(ira->codegen);
|
|
ZigType *optional_type = get_optional_type(ira->codegen, ptr_to_stack_trace_type);
|
|
if (!exec_has_err_ret_trace(ira->codegen, ira->new_irb.exec)) {
|
|
IrInstruction *result = ir_const(ira, &instruction->base, optional_type);
|
|
ConstExprValue *out_val = &result->value;
|
|
assert(get_codegen_ptr_type(optional_type) != nullptr);
|
|
out_val->data.x_ptr.special = ConstPtrSpecialHardCodedAddr;
|
|
out_val->data.x_ptr.data.hard_coded_addr.addr = 0;
|
|
return result;
|
|
}
|
|
IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node, instruction->optional);
|
|
new_instruction->value.type = optional_type;
|
|
return new_instruction;
|
|
} else {
|
|
assert(ira->codegen->have_err_ret_tracing);
|
|
IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node, instruction->optional);
|
|
new_instruction->value.type = get_ptr_to_stack_trace_type(ira->codegen);
|
|
return new_instruction;
|
|
}
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_error_union(IrAnalyze *ira,
|
|
IrInstructionErrorUnion *instruction)
|
|
{
|
|
Error err;
|
|
|
|
ZigType *err_set_type = ir_resolve_type(ira, instruction->err_set->child);
|
|
if (type_is_invalid(err_set_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *payload_type = ir_resolve_type(ira, instruction->payload->child);
|
|
if (type_is_invalid(payload_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (err_set_type->id != ZigTypeIdErrorSet) {
|
|
ir_add_error(ira, instruction->err_set->child,
|
|
buf_sprintf("expected error set type, found type '%s'",
|
|
buf_ptr(&err_set_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if ((err = type_resolve(ira->codegen, payload_type, ResolveStatusSizeKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
ZigType *result_type = get_error_union_type(ira->codegen, err_set_type, payload_type);
|
|
|
|
return ir_const_type(ira, &instruction->base, result_type);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_alloca(IrAnalyze *ira, IrInstruction *source_inst, ZigType *var_type,
|
|
uint32_t align, const char *name_hint, bool force_comptime)
|
|
{
|
|
Error err;
|
|
|
|
ConstExprValue *pointee = create_const_vals(1);
|
|
pointee->special = ConstValSpecialUndef;
|
|
|
|
IrInstructionAllocaGen *result = ir_build_alloca_gen(ira, source_inst, align, name_hint);
|
|
result->base.value.special = ConstValSpecialStatic;
|
|
result->base.value.data.x_ptr.special = ConstPtrSpecialRef;
|
|
result->base.value.data.x_ptr.mut = force_comptime ? ConstPtrMutComptimeVar : ConstPtrMutInfer;
|
|
result->base.value.data.x_ptr.data.ref.pointee = pointee;
|
|
|
|
if ((err = type_resolve(ira->codegen, var_type, ResolveStatusZeroBitsKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
assert(result->base.value.data.x_ptr.special != ConstPtrSpecialInvalid);
|
|
|
|
pointee->type = var_type;
|
|
result->base.value.type = get_pointer_to_type_extra(ira->codegen, var_type, false, false,
|
|
PtrLenSingle, align, 0, 0, false);
|
|
|
|
ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec);
|
|
if (fn_entry != nullptr) {
|
|
fn_entry->alloca_gen_list.append(result);
|
|
}
|
|
result->base.is_gen = true;
|
|
return &result->base;
|
|
}
|
|
|
|
static ZigType *ir_result_loc_expected_type(IrAnalyze *ira, IrInstruction *suspend_source_instr,
|
|
ResultLoc *result_loc)
|
|
{
|
|
switch (result_loc->id) {
|
|
case ResultLocIdInvalid:
|
|
case ResultLocIdPeerParent:
|
|
zig_unreachable();
|
|
case ResultLocIdNone:
|
|
case ResultLocIdVar:
|
|
case ResultLocIdBitCast:
|
|
return nullptr;
|
|
case ResultLocIdInstruction:
|
|
return result_loc->source_instruction->child->value.type;
|
|
case ResultLocIdReturn:
|
|
return ira->explicit_return_type;
|
|
case ResultLocIdPeer:
|
|
return reinterpret_cast<ResultLocPeer*>(result_loc)->parent->resolved_type;
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
static bool type_can_bit_cast(ZigType *t) {
|
|
switch (t->id) {
|
|
case ZigTypeIdInvalid:
|
|
zig_unreachable();
|
|
case ZigTypeIdMetaType:
|
|
case ZigTypeIdOpaque:
|
|
case ZigTypeIdBoundFn:
|
|
case ZigTypeIdArgTuple:
|
|
case ZigTypeIdUnreachable:
|
|
case ZigTypeIdComptimeFloat:
|
|
case ZigTypeIdComptimeInt:
|
|
case ZigTypeIdEnumLiteral:
|
|
case ZigTypeIdUndefined:
|
|
case ZigTypeIdNull:
|
|
case ZigTypeIdPointer:
|
|
return false;
|
|
default:
|
|
// TODO list these types out explicitly, there are probably some other invalid ones here
|
|
return true;
|
|
}
|
|
}
|
|
|
|
static void set_up_result_loc_for_inferred_comptime(IrInstruction *ptr) {
|
|
ConstExprValue *undef_child = create_const_vals(1);
|
|
undef_child->type = ptr->value.type->data.pointer.child_type;
|
|
undef_child->special = ConstValSpecialUndef;
|
|
ptr->value.special = ConstValSpecialStatic;
|
|
ptr->value.data.x_ptr.mut = ConstPtrMutInfer;
|
|
ptr->value.data.x_ptr.special = ConstPtrSpecialRef;
|
|
ptr->value.data.x_ptr.data.ref.pointee = undef_child;
|
|
}
|
|
|
|
// when calling this function, at the callsite must check for result type noreturn and propagate it up
|
|
static IrInstruction *ir_resolve_result_raw(IrAnalyze *ira, IrInstruction *suspend_source_instr,
|
|
ResultLoc *result_loc, ZigType *value_type, IrInstruction *value, bool force_runtime, bool non_null_comptime)
|
|
{
|
|
Error err;
|
|
if (result_loc->resolved_loc != nullptr) {
|
|
// allow to redo the result location if the value is known and comptime and the previous one isn't
|
|
if (value == nullptr || !instr_is_comptime(value) || instr_is_comptime(result_loc->resolved_loc)) {
|
|
return result_loc->resolved_loc;
|
|
}
|
|
}
|
|
result_loc->gen_instruction = value;
|
|
result_loc->implicit_elem_type = value_type;
|
|
switch (result_loc->id) {
|
|
case ResultLocIdInvalid:
|
|
case ResultLocIdPeerParent:
|
|
zig_unreachable();
|
|
case ResultLocIdNone: {
|
|
if (value != nullptr) {
|
|
return nullptr;
|
|
}
|
|
// need to return a result location and don't have one. use a stack allocation
|
|
IrInstructionAllocaGen *alloca_gen = ir_build_alloca_gen(ira, suspend_source_instr, 0, "");
|
|
if ((err = type_resolve(ira->codegen, value_type, ResolveStatusZeroBitsKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
alloca_gen->base.value.type = get_pointer_to_type_extra(ira->codegen, value_type, false, false,
|
|
PtrLenSingle, 0, 0, 0, false);
|
|
set_up_result_loc_for_inferred_comptime(&alloca_gen->base);
|
|
ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec);
|
|
if (fn_entry != nullptr) {
|
|
fn_entry->alloca_gen_list.append(alloca_gen);
|
|
}
|
|
result_loc->written = true;
|
|
result_loc->resolved_loc = &alloca_gen->base;
|
|
return result_loc->resolved_loc;
|
|
}
|
|
case ResultLocIdVar: {
|
|
ResultLocVar *result_loc_var = reinterpret_cast<ResultLocVar *>(result_loc);
|
|
assert(result_loc->source_instruction->id == IrInstructionIdAllocaSrc);
|
|
|
|
if (value_type->id == ZigTypeIdUnreachable || value_type->id == ZigTypeIdOpaque) {
|
|
ir_add_error(ira, result_loc->source_instruction,
|
|
buf_sprintf("variable of type '%s' not allowed", buf_ptr(&value_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstructionAllocaSrc *alloca_src =
|
|
reinterpret_cast<IrInstructionAllocaSrc *>(result_loc->source_instruction);
|
|
bool force_comptime;
|
|
if (!ir_resolve_comptime(ira, alloca_src->is_comptime->child, &force_comptime))
|
|
return ira->codegen->invalid_instruction;
|
|
bool is_comptime = force_comptime || (value != nullptr &&
|
|
value->value.special != ConstValSpecialRuntime && result_loc_var->var->gen_is_const);
|
|
|
|
if (alloca_src->base.child == nullptr || is_comptime) {
|
|
uint32_t align = 0;
|
|
if (alloca_src->align != nullptr && !ir_resolve_align(ira, alloca_src->align->child, &align)) {
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
IrInstruction *alloca_gen;
|
|
if (is_comptime && value != nullptr) {
|
|
if (align > value->value.global_refs->align) {
|
|
value->value.global_refs->align = align;
|
|
}
|
|
alloca_gen = ir_get_ref(ira, result_loc->source_instruction, value, true, false);
|
|
} else {
|
|
alloca_gen = ir_analyze_alloca(ira, result_loc->source_instruction, value_type, align,
|
|
alloca_src->name_hint, force_comptime);
|
|
}
|
|
if (alloca_src->base.child != nullptr) {
|
|
alloca_src->base.child->ref_count = 0;
|
|
}
|
|
alloca_src->base.child = alloca_gen;
|
|
}
|
|
result_loc->written = true;
|
|
result_loc->resolved_loc = is_comptime ? nullptr : alloca_src->base.child;
|
|
return result_loc->resolved_loc;
|
|
}
|
|
case ResultLocIdInstruction: {
|
|
result_loc->written = true;
|
|
result_loc->resolved_loc = result_loc->source_instruction->child;
|
|
return result_loc->resolved_loc;
|
|
}
|
|
case ResultLocIdReturn: {
|
|
if (!non_null_comptime) {
|
|
bool is_comptime = value != nullptr && value->value.special != ConstValSpecialRuntime;
|
|
if (is_comptime)
|
|
return nullptr;
|
|
}
|
|
if ((err = type_resolve(ira->codegen, ira->explicit_return_type, ResolveStatusZeroBitsKnown))) {
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
if (!type_has_bits(ira->explicit_return_type) || !handle_is_ptr(ira->explicit_return_type))
|
|
return nullptr;
|
|
|
|
ZigType *ptr_return_type = get_pointer_to_type(ira->codegen, ira->explicit_return_type, false);
|
|
result_loc->written = true;
|
|
result_loc->resolved_loc = ir_build_return_ptr(ira, result_loc->source_instruction, ptr_return_type);
|
|
if (ir_should_inline(ira->old_irb.exec, result_loc->source_instruction->scope)) {
|
|
set_up_result_loc_for_inferred_comptime(result_loc->resolved_loc);
|
|
}
|
|
return result_loc->resolved_loc;
|
|
}
|
|
case ResultLocIdPeer: {
|
|
ResultLocPeer *result_peer = reinterpret_cast<ResultLocPeer *>(result_loc);
|
|
ResultLocPeerParent *peer_parent = result_peer->parent;
|
|
|
|
if (peer_parent->peers.length == 1) {
|
|
IrInstruction *parent_result_loc = ir_resolve_result(ira, suspend_source_instr, peer_parent->parent,
|
|
value_type, value, force_runtime, non_null_comptime, true);
|
|
result_peer->suspend_pos.basic_block_index = SIZE_MAX;
|
|
result_peer->suspend_pos.instruction_index = SIZE_MAX;
|
|
if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value.type) ||
|
|
parent_result_loc->value.type->id == ZigTypeIdUnreachable)
|
|
{
|
|
return parent_result_loc;
|
|
}
|
|
result_loc->written = true;
|
|
result_loc->resolved_loc = parent_result_loc;
|
|
return result_loc->resolved_loc;
|
|
}
|
|
|
|
bool is_comptime;
|
|
if (!ir_resolve_comptime(ira, peer_parent->is_comptime->child, &is_comptime))
|
|
return ira->codegen->invalid_instruction;
|
|
peer_parent->skipped = is_comptime;
|
|
if (peer_parent->skipped) {
|
|
if (non_null_comptime) {
|
|
return ir_resolve_result(ira, suspend_source_instr, peer_parent->parent,
|
|
value_type, value, force_runtime, non_null_comptime, true);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
if (peer_parent->resolved_type == nullptr) {
|
|
if (peer_parent->end_bb->suspend_instruction_ref == nullptr) {
|
|
peer_parent->end_bb->suspend_instruction_ref = suspend_source_instr;
|
|
}
|
|
IrInstruction *unreach_inst = ira_suspend(ira, suspend_source_instr, result_peer->next_bb,
|
|
&result_peer->suspend_pos);
|
|
if (result_peer->next_bb == nullptr) {
|
|
ir_start_next_bb(ira);
|
|
}
|
|
return unreach_inst;
|
|
}
|
|
|
|
IrInstruction *parent_result_loc = ir_resolve_result(ira, suspend_source_instr, peer_parent->parent,
|
|
peer_parent->resolved_type, nullptr, force_runtime, non_null_comptime, true);
|
|
if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value.type) ||
|
|
parent_result_loc->value.type->id == ZigTypeIdUnreachable)
|
|
{
|
|
return parent_result_loc;
|
|
}
|
|
// because is_comptime is false, we mark this a runtime pointer
|
|
parent_result_loc->value.special = ConstValSpecialRuntime;
|
|
result_loc->written = true;
|
|
result_loc->resolved_loc = parent_result_loc;
|
|
return result_loc->resolved_loc;
|
|
}
|
|
case ResultLocIdBitCast: {
|
|
ResultLocBitCast *result_bit_cast = reinterpret_cast<ResultLocBitCast *>(result_loc);
|
|
ZigType *dest_type = ir_resolve_type(ira, result_bit_cast->base.source_instruction->child);
|
|
if (type_is_invalid(dest_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (get_codegen_ptr_type(dest_type) != nullptr) {
|
|
ir_add_error(ira, result_loc->source_instruction,
|
|
buf_sprintf("unable to @bitCast to pointer type '%s'", buf_ptr(&dest_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (!type_can_bit_cast(dest_type)) {
|
|
ir_add_error(ira, result_loc->source_instruction,
|
|
buf_sprintf("unable to @bitCast to type '%s'", buf_ptr(&dest_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (get_codegen_ptr_type(value_type) != nullptr) {
|
|
ir_add_error(ira, suspend_source_instr,
|
|
buf_sprintf("unable to @bitCast from pointer type '%s'", buf_ptr(&value_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (!type_can_bit_cast(value_type)) {
|
|
ir_add_error(ira, suspend_source_instr,
|
|
buf_sprintf("unable to @bitCast from type '%s'", buf_ptr(&value_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *bitcasted_value;
|
|
if (value != nullptr) {
|
|
bitcasted_value = ir_analyze_bit_cast(ira, result_loc->source_instruction, value, dest_type);
|
|
} else {
|
|
bitcasted_value = nullptr;
|
|
}
|
|
|
|
IrInstruction *parent_result_loc = ir_resolve_result(ira, suspend_source_instr, result_bit_cast->parent,
|
|
dest_type, bitcasted_value, force_runtime, non_null_comptime, true);
|
|
if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value.type) ||
|
|
parent_result_loc->value.type->id == ZigTypeIdUnreachable)
|
|
{
|
|
return parent_result_loc;
|
|
}
|
|
ZigType *parent_ptr_type = parent_result_loc->value.type;
|
|
assert(parent_ptr_type->id == ZigTypeIdPointer);
|
|
if ((err = type_resolve(ira->codegen, parent_ptr_type->data.pointer.child_type,
|
|
ResolveStatusAlignmentKnown)))
|
|
{
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
uint64_t parent_ptr_align = get_ptr_align(ira->codegen, parent_ptr_type);
|
|
ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, value_type,
|
|
parent_ptr_type->data.pointer.is_const, parent_ptr_type->data.pointer.is_volatile, PtrLenSingle,
|
|
parent_ptr_align, 0, 0, parent_ptr_type->data.pointer.allow_zero);
|
|
|
|
result_loc->written = true;
|
|
result_loc->resolved_loc = ir_analyze_ptr_cast(ira, suspend_source_instr, parent_result_loc,
|
|
ptr_type, result_bit_cast->base.source_instruction, false);
|
|
return result_loc->resolved_loc;
|
|
}
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
static IrInstruction *ir_resolve_result(IrAnalyze *ira, IrInstruction *suspend_source_instr,
|
|
ResultLoc *result_loc_pass1, ZigType *value_type, IrInstruction *value, bool force_runtime,
|
|
bool non_null_comptime, bool allow_discard)
|
|
{
|
|
if (!allow_discard && result_loc_pass1->id == ResultLocIdInstruction &&
|
|
instr_is_comptime(result_loc_pass1->source_instruction) &&
|
|
result_loc_pass1->source_instruction->value.type->id == ZigTypeIdPointer &&
|
|
result_loc_pass1->source_instruction->value.data.x_ptr.special == ConstPtrSpecialDiscard)
|
|
{
|
|
result_loc_pass1 = no_result_loc();
|
|
}
|
|
IrInstruction *result_loc = ir_resolve_result_raw(ira, suspend_source_instr, result_loc_pass1, value_type,
|
|
value, force_runtime, non_null_comptime);
|
|
if (result_loc == nullptr || (instr_is_unreachable(result_loc) || type_is_invalid(result_loc->value.type)))
|
|
return result_loc;
|
|
|
|
if ((force_runtime || (value != nullptr && !instr_is_comptime(value))) &&
|
|
result_loc_pass1->written && result_loc->value.data.x_ptr.mut == ConstPtrMutInfer)
|
|
{
|
|
result_loc->value.special = ConstValSpecialRuntime;
|
|
}
|
|
|
|
ir_assert(result_loc->value.type->id == ZigTypeIdPointer, suspend_source_instr);
|
|
ZigType *actual_elem_type = result_loc->value.type->data.pointer.child_type;
|
|
if (actual_elem_type->id == ZigTypeIdOptional && value_type->id != ZigTypeIdOptional &&
|
|
value_type->id != ZigTypeIdNull)
|
|
{
|
|
return ir_analyze_unwrap_optional_payload(ira, suspend_source_instr, result_loc, false, true);
|
|
} else if (actual_elem_type->id == ZigTypeIdErrorUnion && value_type->id != ZigTypeIdErrorUnion) {
|
|
if (value_type->id == ZigTypeIdErrorSet) {
|
|
return ir_analyze_unwrap_err_code(ira, suspend_source_instr, result_loc, true);
|
|
} else {
|
|
IrInstruction *unwrapped_err_ptr = ir_analyze_unwrap_error_payload(ira, suspend_source_instr,
|
|
result_loc, false, true);
|
|
ZigType *actual_payload_type = actual_elem_type->data.error_union.payload_type;
|
|
if (actual_payload_type->id == ZigTypeIdOptional && value_type->id != ZigTypeIdOptional) {
|
|
return ir_analyze_unwrap_optional_payload(ira, suspend_source_instr, unwrapped_err_ptr, false, true);
|
|
} else {
|
|
return unwrapped_err_ptr;
|
|
}
|
|
}
|
|
} else if (is_slice(actual_elem_type) && value_type->id == ZigTypeIdArray) {
|
|
// need to allow EndExpr to do the implicit cast from array to slice
|
|
result_loc_pass1->written = false;
|
|
}
|
|
return result_loc;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_implicit_cast(IrAnalyze *ira, IrInstructionImplicitCast *instruction) {
|
|
ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child);
|
|
if (type_is_invalid(dest_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *target = instruction->target->child;
|
|
if (type_is_invalid(target->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
return ir_implicit_cast_with_result(ira, target, dest_type, instruction->result_loc);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_resolve_result(IrAnalyze *ira, IrInstructionResolveResult *instruction) {
|
|
ZigType *implicit_elem_type = ir_resolve_type(ira, instruction->ty->child);
|
|
if (type_is_invalid(implicit_elem_type))
|
|
return ira->codegen->invalid_instruction;
|
|
IrInstruction *result_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc,
|
|
implicit_elem_type, nullptr, false, true, true);
|
|
if (result_loc != nullptr)
|
|
return result_loc;
|
|
|
|
ZigFn *fn = exec_fn_entry(ira->new_irb.exec);
|
|
if (fn != nullptr && fn->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync &&
|
|
instruction->result_loc->id == ResultLocIdReturn)
|
|
{
|
|
result_loc = ir_resolve_result(ira, &instruction->base, no_result_loc(),
|
|
implicit_elem_type, nullptr, false, true, true);
|
|
if (result_loc != nullptr &&
|
|
(type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc)))
|
|
{
|
|
return result_loc;
|
|
}
|
|
result_loc->value.special = ConstValSpecialRuntime;
|
|
return result_loc;
|
|
}
|
|
|
|
IrInstruction *result = ir_const(ira, &instruction->base, implicit_elem_type);
|
|
result->value.special = ConstValSpecialUndef;
|
|
IrInstruction *ptr = ir_get_ref(ira, &instruction->base, result, false, false);
|
|
ptr->value.data.x_ptr.mut = ConstPtrMutComptimeVar;
|
|
return ptr;
|
|
}
|
|
|
|
static void ir_reset_result(ResultLoc *result_loc) {
|
|
result_loc->written = false;
|
|
result_loc->resolved_loc = nullptr;
|
|
result_loc->gen_instruction = nullptr;
|
|
result_loc->implicit_elem_type = nullptr;
|
|
switch (result_loc->id) {
|
|
case ResultLocIdInvalid:
|
|
zig_unreachable();
|
|
case ResultLocIdPeerParent: {
|
|
ResultLocPeerParent *peer_parent = reinterpret_cast<ResultLocPeerParent *>(result_loc);
|
|
peer_parent->skipped = false;
|
|
peer_parent->done_resuming = false;
|
|
peer_parent->resolved_type = nullptr;
|
|
for (size_t i = 0; i < peer_parent->peers.length; i += 1) {
|
|
ir_reset_result(&peer_parent->peers.at(i)->base);
|
|
}
|
|
break;
|
|
}
|
|
case ResultLocIdVar: {
|
|
IrInstructionAllocaSrc *alloca_src =
|
|
reinterpret_cast<IrInstructionAllocaSrc *>(result_loc->source_instruction);
|
|
alloca_src->base.child = nullptr;
|
|
break;
|
|
}
|
|
case ResultLocIdPeer:
|
|
case ResultLocIdNone:
|
|
case ResultLocIdReturn:
|
|
case ResultLocIdInstruction:
|
|
case ResultLocIdBitCast:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_reset_result(IrAnalyze *ira, IrInstructionResetResult *instruction) {
|
|
ir_reset_result(instruction->result_loc);
|
|
return ir_const_void(ira, &instruction->base);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCallSrc *call_instruction, ZigFn *fn_entry,
|
|
ZigType *fn_type, IrInstruction *fn_ref, IrInstruction **casted_args, size_t arg_count,
|
|
IrInstruction *casted_new_stack)
|
|
{
|
|
if (casted_new_stack != nullptr) {
|
|
// this is an @asyncCall
|
|
|
|
if (fn_type->data.fn.fn_type_id.cc != CallingConventionAsync) {
|
|
ir_add_error(ira, fn_ref,
|
|
buf_sprintf("expected async function, found '%s'", buf_ptr(&fn_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *ret_ptr = call_instruction->args[call_instruction->arg_count]->child;
|
|
if (type_is_invalid(ret_ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *anyframe_type = get_any_frame_type(ira->codegen, fn_type->data.fn.fn_type_id.return_type);
|
|
|
|
IrInstructionCallGen *call_gen = ir_build_call_gen(ira, &call_instruction->base, nullptr, fn_ref,
|
|
arg_count, casted_args, FnInlineAuto, true, casted_new_stack, ret_ptr, anyframe_type);
|
|
return &call_gen->base;
|
|
} else if (fn_entry == nullptr) {
|
|
ir_add_error(ira, fn_ref, buf_sprintf("function is not comptime-known; @asyncCall required"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
ZigType *frame_type = get_coro_frame_type(ira->codegen, fn_entry);
|
|
IrInstruction *result_loc = ir_resolve_result(ira, &call_instruction->base, call_instruction->result_loc,
|
|
frame_type, nullptr, true, true, false);
|
|
if (result_loc != nullptr && (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc))) {
|
|
return result_loc;
|
|
}
|
|
return &ir_build_call_gen(ira, &call_instruction->base, fn_entry, fn_ref, arg_count,
|
|
casted_args, FnInlineAuto, true, nullptr, result_loc, frame_type)->base;
|
|
}
|
|
static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node,
|
|
IrInstruction *arg, Scope **exec_scope, size_t *next_proto_i)
|
|
{
|
|
AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(*next_proto_i);
|
|
assert(param_decl_node->type == NodeTypeParamDecl);
|
|
|
|
IrInstruction *casted_arg;
|
|
if (param_decl_node->data.param_decl.var_token == nullptr) {
|
|
AstNode *param_type_node = param_decl_node->data.param_decl.type;
|
|
ZigType *param_type = ir_analyze_type_expr(ira, *exec_scope, param_type_node);
|
|
if (type_is_invalid(param_type))
|
|
return false;
|
|
|
|
casted_arg = ir_implicit_cast(ira, arg, param_type);
|
|
if (type_is_invalid(casted_arg->value.type))
|
|
return false;
|
|
} else {
|
|
casted_arg = arg;
|
|
}
|
|
|
|
ConstExprValue *arg_val = ir_resolve_const(ira, casted_arg, UndefOk);
|
|
if (!arg_val)
|
|
return false;
|
|
|
|
Buf *param_name = param_decl_node->data.param_decl.name;
|
|
ZigVar *var = add_variable(ira->codegen, param_decl_node,
|
|
*exec_scope, param_name, true, arg_val, nullptr, arg_val->type);
|
|
*exec_scope = var->child_scope;
|
|
*next_proto_i += 1;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_node,
|
|
IrInstruction *arg, Scope **child_scope, size_t *next_proto_i,
|
|
GenericFnTypeId *generic_id, FnTypeId *fn_type_id, IrInstruction **casted_args,
|
|
ZigFn *impl_fn)
|
|
{
|
|
AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(*next_proto_i);
|
|
assert(param_decl_node->type == NodeTypeParamDecl);
|
|
bool is_var_args = param_decl_node->data.param_decl.is_var_args;
|
|
bool arg_part_of_generic_id = false;
|
|
IrInstruction *casted_arg;
|
|
if (is_var_args) {
|
|
arg_part_of_generic_id = true;
|
|
casted_arg = arg;
|
|
} else {
|
|
if (param_decl_node->data.param_decl.var_token == nullptr) {
|
|
AstNode *param_type_node = param_decl_node->data.param_decl.type;
|
|
ZigType *param_type = ir_analyze_type_expr(ira, *child_scope, param_type_node);
|
|
if (type_is_invalid(param_type))
|
|
return false;
|
|
|
|
casted_arg = ir_implicit_cast(ira, arg, param_type);
|
|
if (type_is_invalid(casted_arg->value.type))
|
|
return false;
|
|
} else {
|
|
arg_part_of_generic_id = true;
|
|
casted_arg = arg;
|
|
}
|
|
}
|
|
|
|
bool comptime_arg = param_decl_node->data.param_decl.is_inline ||
|
|
casted_arg->value.type->id == ZigTypeIdComptimeInt || casted_arg->value.type->id == ZigTypeIdComptimeFloat;
|
|
|
|
ConstExprValue *arg_val;
|
|
|
|
if (comptime_arg) {
|
|
arg_part_of_generic_id = true;
|
|
arg_val = ir_resolve_const(ira, casted_arg, UndefBad);
|
|
if (!arg_val)
|
|
return false;
|
|
} else {
|
|
arg_val = create_const_runtime(casted_arg->value.type);
|
|
}
|
|
if (arg_part_of_generic_id) {
|
|
copy_const_val(&generic_id->params[generic_id->param_count], arg_val, true);
|
|
generic_id->param_count += 1;
|
|
}
|
|
|
|
Buf *param_name = param_decl_node->data.param_decl.name;
|
|
if (!param_name) return false;
|
|
if (!is_var_args) {
|
|
ZigVar *var = add_variable(ira->codegen, param_decl_node,
|
|
*child_scope, param_name, true, arg_val, nullptr, arg_val->type);
|
|
*child_scope = var->child_scope;
|
|
var->shadowable = !comptime_arg;
|
|
|
|
*next_proto_i += 1;
|
|
} else if (casted_arg->value.type->id == ZigTypeIdComptimeInt ||
|
|
casted_arg->value.type->id == ZigTypeIdComptimeFloat)
|
|
{
|
|
ir_add_error(ira, casted_arg,
|
|
buf_sprintf("compiler bug: integer and float literals in var args function must be casted. https://github.com/ziglang/zig/issues/557"));
|
|
return false;
|
|
}
|
|
|
|
if (!comptime_arg) {
|
|
switch (type_requires_comptime(ira->codegen, casted_arg->value.type)) {
|
|
case ReqCompTimeYes:
|
|
ir_add_error(ira, casted_arg,
|
|
buf_sprintf("parameter of type '%s' requires comptime", buf_ptr(&casted_arg->value.type->name)));
|
|
return false;
|
|
case ReqCompTimeInvalid:
|
|
return false;
|
|
case ReqCompTimeNo:
|
|
break;
|
|
}
|
|
|
|
casted_args[fn_type_id->param_count] = casted_arg;
|
|
FnTypeParamInfo *param_info = &fn_type_id->param_info[fn_type_id->param_count];
|
|
param_info->type = casted_arg->value.type;
|
|
param_info->is_noalias = param_decl_node->data.param_decl.is_noalias;
|
|
impl_fn->param_source_nodes[fn_type_id->param_count] = param_decl_node;
|
|
fn_type_id->param_count += 1;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static ZigVar *get_fn_var_by_index(ZigFn *fn_entry, size_t index) {
|
|
FnTypeParamInfo *src_param_info = &fn_entry->type_entry->data.fn.fn_type_id.param_info[index];
|
|
if (!type_has_bits(src_param_info->type))
|
|
return nullptr;
|
|
|
|
size_t next_var_i = 0;
|
|
for (size_t param_i = 0; param_i < index; param_i += 1) {
|
|
FnTypeParamInfo *src_param_info = &fn_entry->type_entry->data.fn.fn_type_id.param_info[param_i];
|
|
if (!type_has_bits(src_param_info->type)) {
|
|
continue;
|
|
}
|
|
|
|
next_var_i += 1;
|
|
}
|
|
return fn_entry->variable_list.at(next_var_i);
|
|
}
|
|
|
|
static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, ZigVar *var) {
|
|
while (var->next_var != nullptr) {
|
|
var = var->next_var;
|
|
}
|
|
|
|
if (var->mem_slot_index != SIZE_MAX && var->owner_exec->analysis == nullptr) {
|
|
assert(ira->codegen->errors.length != 0);
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
if (var->var_type == nullptr || type_is_invalid(var->var_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ConstExprValue *mem_slot = nullptr;
|
|
|
|
bool comptime_var_mem = ir_get_var_is_comptime(var);
|
|
bool linkage_makes_it_runtime = var->decl_node->data.variable_declaration.is_extern;
|
|
bool is_const = var->src_is_const;
|
|
bool is_volatile = false;
|
|
|
|
if (linkage_makes_it_runtime)
|
|
goto no_mem_slot;
|
|
|
|
if (var->const_value->special == ConstValSpecialStatic) {
|
|
mem_slot = var->const_value;
|
|
} else {
|
|
if (var->mem_slot_index != SIZE_MAX && (comptime_var_mem || var->gen_is_const)) {
|
|
// find the relevant exec_context
|
|
assert(var->owner_exec != nullptr);
|
|
assert(var->owner_exec->analysis != nullptr);
|
|
IrExecContext *exec_context = &var->owner_exec->analysis->exec_context;
|
|
assert(var->mem_slot_index < exec_context->mem_slot_list.length);
|
|
mem_slot = exec_context->mem_slot_list.at(var->mem_slot_index);
|
|
}
|
|
}
|
|
|
|
if (mem_slot != nullptr) {
|
|
switch (mem_slot->special) {
|
|
case ConstValSpecialRuntime:
|
|
goto no_mem_slot;
|
|
case ConstValSpecialStatic: // fallthrough
|
|
case ConstValSpecialUndef: {
|
|
ConstPtrMut ptr_mut;
|
|
if (comptime_var_mem) {
|
|
ptr_mut = ConstPtrMutComptimeVar;
|
|
} else if (var->gen_is_const) {
|
|
ptr_mut = ConstPtrMutComptimeConst;
|
|
} else {
|
|
assert(!comptime_var_mem);
|
|
ptr_mut = ConstPtrMutRuntimeVar;
|
|
}
|
|
return ir_get_const_ptr(ira, instruction, mem_slot, var->var_type,
|
|
ptr_mut, is_const, is_volatile, var->align_bytes);
|
|
}
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
no_mem_slot:
|
|
|
|
IrInstruction *var_ptr_instruction = ir_build_var_ptr(&ira->new_irb,
|
|
instruction->scope, instruction->source_node, var);
|
|
var_ptr_instruction->value.type = get_pointer_to_type_extra(ira->codegen, var->var_type,
|
|
var->src_is_const, is_volatile, PtrLenSingle, var->align_bytes, 0, 0, false);
|
|
|
|
bool in_fn_scope = (scope_fn_entry(var->parent_scope) != nullptr);
|
|
var_ptr_instruction->value.data.rh_ptr = in_fn_scope ? RuntimeHintPtrStack : RuntimeHintPtrNonStack;
|
|
|
|
return var_ptr_instruction;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr,
|
|
IrInstruction *ptr, IrInstruction *uncasted_value, bool allow_write_through_const)
|
|
{
|
|
assert(ptr->value.type->id == ZigTypeIdPointer);
|
|
|
|
if (ptr->value.data.x_ptr.special == ConstPtrSpecialDiscard) {
|
|
if (uncasted_value->value.type->id == ZigTypeIdErrorUnion ||
|
|
uncasted_value->value.type->id == ZigTypeIdErrorSet)
|
|
{
|
|
ir_add_error(ira, source_instr, buf_sprintf("error is discarded"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
return ir_const_void(ira, source_instr);
|
|
}
|
|
|
|
ZigType *child_type = ptr->value.type->data.pointer.child_type;
|
|
|
|
if (ptr->value.type->data.pointer.is_const && !allow_write_through_const) {
|
|
ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *value = ir_implicit_cast(ira, uncasted_value, child_type);
|
|
if (value == ira->codegen->invalid_instruction)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
switch (type_has_one_possible_value(ira->codegen, child_type)) {
|
|
case OnePossibleValueInvalid:
|
|
return ira->codegen->invalid_instruction;
|
|
case OnePossibleValueYes:
|
|
return ir_const_void(ira, source_instr);
|
|
case OnePossibleValueNo:
|
|
break;
|
|
}
|
|
|
|
if (instr_is_comptime(ptr) && ptr->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr) {
|
|
if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst) {
|
|
ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar ||
|
|
ptr->value.data.x_ptr.mut == ConstPtrMutInfer)
|
|
{
|
|
if (instr_is_comptime(value)) {
|
|
ConstExprValue *dest_val = const_ptr_pointee(ira, ira->codegen, &ptr->value, source_instr->source_node);
|
|
if (dest_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
if (dest_val->special != ConstValSpecialRuntime) {
|
|
// TODO this allows a value stored to have the original value modified and then
|
|
// have that affect what should be a copy. We need some kind of advanced copy-on-write
|
|
// system to make these two tests pass at the same time:
|
|
// * "string literal used as comptime slice is memoized"
|
|
// * "comptime modification of const struct field" - except modified to avoid
|
|
// ConstPtrMutComptimeVar, thus defeating the logic below.
|
|
bool same_global_refs = ptr->value.data.x_ptr.mut != ConstPtrMutComptimeVar;
|
|
copy_const_val(dest_val, &value->value, same_global_refs);
|
|
if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar &&
|
|
!ira->new_irb.current_basic_block->must_be_comptime_source_instr)
|
|
{
|
|
ira->new_irb.current_basic_block->must_be_comptime_source_instr = source_instr;
|
|
}
|
|
return ir_const_void(ira, source_instr);
|
|
}
|
|
}
|
|
if (ptr->value.data.x_ptr.mut == ConstPtrMutInfer) {
|
|
ptr->value.special = ConstValSpecialRuntime;
|
|
} else {
|
|
ir_add_error(ira, source_instr,
|
|
buf_sprintf("cannot store runtime value in compile time variable"));
|
|
ConstExprValue *dest_val = const_ptr_pointee_unchecked(ira->codegen, &ptr->value);
|
|
dest_val->type = ira->codegen->builtin_types.entry_invalid;
|
|
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (type_requires_comptime(ira->codegen, child_type)) {
|
|
case ReqCompTimeInvalid:
|
|
return ira->codegen->invalid_instruction;
|
|
case ReqCompTimeYes:
|
|
switch (type_has_one_possible_value(ira->codegen, ptr->value.type)) {
|
|
case OnePossibleValueInvalid:
|
|
return ira->codegen->invalid_instruction;
|
|
case OnePossibleValueNo:
|
|
ir_add_error(ira, source_instr,
|
|
buf_sprintf("cannot store runtime value in type '%s'", buf_ptr(&child_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
case OnePossibleValueYes:
|
|
return ir_const_void(ira, source_instr);
|
|
}
|
|
zig_unreachable();
|
|
case ReqCompTimeNo:
|
|
break;
|
|
}
|
|
|
|
IrInstructionStorePtr *store_ptr = ir_build_store_ptr(&ira->new_irb, source_instr->scope,
|
|
source_instr->source_node, ptr, value);
|
|
return &store_ptr->base;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *call_instruction,
|
|
ZigFn *fn_entry, ZigType *fn_type, IrInstruction *fn_ref,
|
|
IrInstruction *first_arg_ptr, bool comptime_fn_call, FnInline fn_inline)
|
|
{
|
|
Error err;
|
|
FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
|
|
size_t first_arg_1_or_0 = first_arg_ptr ? 1 : 0;
|
|
|
|
// for extern functions, the var args argument is not counted.
|
|
// for zig functions, it is.
|
|
size_t var_args_1_or_0;
|
|
if (fn_type_id->cc == CallingConventionC) {
|
|
var_args_1_or_0 = 0;
|
|
} else {
|
|
var_args_1_or_0 = fn_type_id->is_var_args ? 1 : 0;
|
|
}
|
|
size_t src_param_count = fn_type_id->param_count - var_args_1_or_0;
|
|
|
|
size_t call_param_count = call_instruction->arg_count + first_arg_1_or_0;
|
|
for (size_t i = 0; i < call_instruction->arg_count; i += 1) {
|
|
ConstExprValue *arg_tuple_value = &call_instruction->args[i]->child->value;
|
|
if (arg_tuple_value->type->id == ZigTypeIdArgTuple) {
|
|
call_param_count -= 1;
|
|
call_param_count += arg_tuple_value->data.x_arg_tuple.end_index -
|
|
arg_tuple_value->data.x_arg_tuple.start_index;
|
|
}
|
|
}
|
|
AstNode *source_node = call_instruction->base.source_node;
|
|
|
|
AstNode *fn_proto_node = fn_entry ? fn_entry->proto_node : nullptr;;
|
|
|
|
if (fn_type_id->cc == CallingConventionNaked) {
|
|
ErrorMsg *msg = ir_add_error(ira, fn_ref, buf_sprintf("unable to call function with naked calling convention"));
|
|
if (fn_proto_node) {
|
|
add_error_note(ira->codegen, msg, fn_proto_node, buf_sprintf("declared here"));
|
|
}
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
|
|
if (fn_type_id->is_var_args) {
|
|
if (call_param_count < src_param_count) {
|
|
ErrorMsg *msg = ir_add_error_node(ira, source_node,
|
|
buf_sprintf("expected at least %" ZIG_PRI_usize " arguments, found %" ZIG_PRI_usize "", src_param_count, call_param_count));
|
|
if (fn_proto_node) {
|
|
add_error_note(ira->codegen, msg, fn_proto_node,
|
|
buf_sprintf("declared here"));
|
|
}
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
} else if (src_param_count != call_param_count) {
|
|
ErrorMsg *msg = ir_add_error_node(ira, source_node,
|
|
buf_sprintf("expected %" ZIG_PRI_usize " arguments, found %" ZIG_PRI_usize "", src_param_count, call_param_count));
|
|
if (fn_proto_node) {
|
|
add_error_note(ira->codegen, msg, fn_proto_node,
|
|
buf_sprintf("declared here"));
|
|
}
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (comptime_fn_call) {
|
|
// No special handling is needed for compile time evaluation of generic functions.
|
|
if (!fn_entry || fn_entry->body_node == nullptr) {
|
|
ir_add_error(ira, fn_ref, buf_sprintf("unable to evaluate constant expression"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (!ir_emit_backward_branch(ira, &call_instruction->base))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
// Fork a scope of the function with known values for the parameters.
|
|
Scope *exec_scope = &fn_entry->fndef_scope->base;
|
|
|
|
size_t next_proto_i = 0;
|
|
if (first_arg_ptr) {
|
|
assert(first_arg_ptr->value.type->id == ZigTypeIdPointer);
|
|
|
|
bool first_arg_known_bare = false;
|
|
if (fn_type_id->next_param_index >= 1) {
|
|
ZigType *param_type = fn_type_id->param_info[next_proto_i].type;
|
|
if (type_is_invalid(param_type))
|
|
return ira->codegen->invalid_instruction;
|
|
first_arg_known_bare = param_type->id != ZigTypeIdPointer;
|
|
}
|
|
|
|
IrInstruction *first_arg;
|
|
if (!first_arg_known_bare && handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type)) {
|
|
first_arg = first_arg_ptr;
|
|
} else {
|
|
first_arg = ir_get_deref(ira, first_arg_ptr, first_arg_ptr, nullptr);
|
|
if (type_is_invalid(first_arg->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (!ir_analyze_fn_call_inline_arg(ira, fn_proto_node, first_arg, &exec_scope, &next_proto_i))
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (fn_proto_node->data.fn_proto.is_var_args) {
|
|
ir_add_error(ira, &call_instruction->base,
|
|
buf_sprintf("compiler bug: unable to call var args function at compile time. https://github.com/ziglang/zig/issues/313"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
|
|
for (size_t call_i = 0; call_i < call_instruction->arg_count; call_i += 1) {
|
|
IrInstruction *old_arg = call_instruction->args[call_i]->child;
|
|
if (type_is_invalid(old_arg->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (!ir_analyze_fn_call_inline_arg(ira, fn_proto_node, old_arg, &exec_scope, &next_proto_i))
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type;
|
|
ZigType *specified_return_type = ir_analyze_type_expr(ira, exec_scope, return_type_node);
|
|
if (type_is_invalid(specified_return_type))
|
|
return ira->codegen->invalid_instruction;
|
|
ZigType *return_type;
|
|
ZigType *inferred_err_set_type = nullptr;
|
|
if (fn_proto_node->data.fn_proto.auto_err_set) {
|
|
inferred_err_set_type = get_auto_err_set_type(ira->codegen, fn_entry);
|
|
if ((err = type_resolve(ira->codegen, specified_return_type, ResolveStatusSizeKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
return_type = get_error_union_type(ira->codegen, inferred_err_set_type, specified_return_type);
|
|
} else {
|
|
return_type = specified_return_type;
|
|
}
|
|
|
|
bool cacheable = fn_eval_cacheable(exec_scope, return_type);
|
|
ConstExprValue *result = nullptr;
|
|
if (cacheable) {
|
|
auto entry = ira->codegen->memoized_fn_eval_table.maybe_get(exec_scope);
|
|
if (entry)
|
|
result = entry->value;
|
|
}
|
|
|
|
if (result == nullptr) {
|
|
// Analyze the fn body block like any other constant expression.
|
|
AstNode *body_node = fn_entry->body_node;
|
|
result = ir_eval_const_value(ira->codegen, exec_scope, body_node, return_type,
|
|
ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, fn_entry,
|
|
nullptr, call_instruction->base.source_node, nullptr, ira->new_irb.exec, return_type_node);
|
|
|
|
if (inferred_err_set_type != nullptr) {
|
|
inferred_err_set_type->data.error_set.infer_fn = nullptr;
|
|
if (result->type->id == ZigTypeIdErrorUnion) {
|
|
ErrorTableEntry *err = result->data.x_err_union.error_set->data.x_err_set;
|
|
if (err != nullptr) {
|
|
inferred_err_set_type->data.error_set.err_count = 1;
|
|
inferred_err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(1);
|
|
inferred_err_set_type->data.error_set.errors[0] = err;
|
|
}
|
|
ZigType *fn_inferred_err_set_type = result->type->data.error_union.err_set_type;
|
|
inferred_err_set_type->data.error_set.err_count = fn_inferred_err_set_type->data.error_set.err_count;
|
|
inferred_err_set_type->data.error_set.errors = fn_inferred_err_set_type->data.error_set.errors;
|
|
} else if (result->type->id == ZigTypeIdErrorSet) {
|
|
inferred_err_set_type->data.error_set.err_count = result->type->data.error_set.err_count;
|
|
inferred_err_set_type->data.error_set.errors = result->type->data.error_set.errors;
|
|
}
|
|
}
|
|
|
|
if (cacheable) {
|
|
ira->codegen->memoized_fn_eval_table.put(exec_scope, result);
|
|
}
|
|
|
|
if (type_is_invalid(result->type))
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *new_instruction = ir_const(ira, &call_instruction->base, result->type);
|
|
copy_const_val(&new_instruction->value, result, true);
|
|
new_instruction->value.type = return_type;
|
|
return ir_finish_anal(ira, new_instruction);
|
|
}
|
|
|
|
IrInstruction *casted_new_stack = nullptr;
|
|
if (call_instruction->new_stack != nullptr) {
|
|
ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,
|
|
false, false, PtrLenUnknown, 0, 0, 0, false);
|
|
ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr);
|
|
IrInstruction *new_stack = call_instruction->new_stack->child;
|
|
if (type_is_invalid(new_stack->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
casted_new_stack = ir_implicit_cast(ira, new_stack, u8_slice);
|
|
if (type_is_invalid(casted_new_stack->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (fn_type->data.fn.is_generic) {
|
|
if (!fn_entry) {
|
|
ir_add_error(ira, call_instruction->fn_ref,
|
|
buf_sprintf("calling a generic function requires compile-time known function value"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
// Count the arguments of the function type id we are creating
|
|
size_t new_fn_arg_count = first_arg_1_or_0;
|
|
for (size_t call_i = 0; call_i < call_instruction->arg_count; call_i += 1) {
|
|
IrInstruction *arg = call_instruction->args[call_i]->child;
|
|
if (type_is_invalid(arg->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (arg->value.type->id == ZigTypeIdArgTuple) {
|
|
new_fn_arg_count += arg->value.data.x_arg_tuple.end_index - arg->value.data.x_arg_tuple.start_index;
|
|
} else {
|
|
new_fn_arg_count += 1;
|
|
}
|
|
}
|
|
|
|
IrInstruction **casted_args = allocate<IrInstruction *>(new_fn_arg_count);
|
|
|
|
// Fork a scope of the function with known values for the parameters.
|
|
Scope *parent_scope = fn_entry->fndef_scope->base.parent;
|
|
ZigFn *impl_fn = create_fn(ira->codegen, fn_proto_node);
|
|
impl_fn->param_source_nodes = allocate<AstNode *>(new_fn_arg_count);
|
|
buf_init_from_buf(&impl_fn->symbol_name, &fn_entry->symbol_name);
|
|
impl_fn->fndef_scope = create_fndef_scope(ira->codegen, impl_fn->body_node, parent_scope, impl_fn);
|
|
impl_fn->child_scope = &impl_fn->fndef_scope->base;
|
|
FnTypeId inst_fn_type_id = {0};
|
|
init_fn_type_id(&inst_fn_type_id, fn_proto_node, new_fn_arg_count);
|
|
inst_fn_type_id.param_count = 0;
|
|
inst_fn_type_id.is_var_args = false;
|
|
|
|
// TODO maybe GenericFnTypeId can be replaced with using the child_scope directly
|
|
// as the key in generic_table
|
|
GenericFnTypeId *generic_id = allocate<GenericFnTypeId>(1);
|
|
generic_id->fn_entry = fn_entry;
|
|
generic_id->param_count = 0;
|
|
generic_id->params = create_const_vals(new_fn_arg_count);
|
|
size_t next_proto_i = 0;
|
|
|
|
if (first_arg_ptr) {
|
|
assert(first_arg_ptr->value.type->id == ZigTypeIdPointer);
|
|
|
|
bool first_arg_known_bare = false;
|
|
if (fn_type_id->next_param_index >= 1) {
|
|
ZigType *param_type = fn_type_id->param_info[next_proto_i].type;
|
|
if (type_is_invalid(param_type))
|
|
return ira->codegen->invalid_instruction;
|
|
first_arg_known_bare = param_type->id != ZigTypeIdPointer;
|
|
}
|
|
|
|
IrInstruction *first_arg;
|
|
if (!first_arg_known_bare && handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type)) {
|
|
first_arg = first_arg_ptr;
|
|
} else {
|
|
first_arg = ir_get_deref(ira, first_arg_ptr, first_arg_ptr, nullptr);
|
|
if (type_is_invalid(first_arg->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (!ir_analyze_fn_call_generic_arg(ira, fn_proto_node, first_arg, &impl_fn->child_scope,
|
|
&next_proto_i, generic_id, &inst_fn_type_id, casted_args, impl_fn))
|
|
{
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
|
|
bool found_first_var_arg = false;
|
|
size_t first_var_arg;
|
|
|
|
ZigFn *parent_fn_entry = exec_fn_entry(ira->new_irb.exec);
|
|
assert(parent_fn_entry);
|
|
for (size_t call_i = 0; call_i < call_instruction->arg_count; call_i += 1) {
|
|
IrInstruction *arg = call_instruction->args[call_i]->child;
|
|
if (type_is_invalid(arg->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (arg->value.type->id == ZigTypeIdArgTuple) {
|
|
for (size_t arg_tuple_i = arg->value.data.x_arg_tuple.start_index;
|
|
arg_tuple_i < arg->value.data.x_arg_tuple.end_index; arg_tuple_i += 1)
|
|
{
|
|
AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(next_proto_i);
|
|
assert(param_decl_node->type == NodeTypeParamDecl);
|
|
bool is_var_args = param_decl_node->data.param_decl.is_var_args;
|
|
if (is_var_args && !found_first_var_arg) {
|
|
first_var_arg = inst_fn_type_id.param_count;
|
|
found_first_var_arg = true;
|
|
}
|
|
|
|
ZigVar *arg_var = get_fn_var_by_index(parent_fn_entry, arg_tuple_i);
|
|
if (arg_var == nullptr) {
|
|
ir_add_error(ira, arg,
|
|
buf_sprintf("compiler bug: var args can't handle void. https://github.com/ziglang/zig/issues/557"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
IrInstruction *arg_var_ptr_inst = ir_get_var_ptr(ira, arg, arg_var);
|
|
if (type_is_invalid(arg_var_ptr_inst->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *arg_tuple_arg = ir_get_deref(ira, arg, arg_var_ptr_inst, nullptr);
|
|
if (type_is_invalid(arg_tuple_arg->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (!ir_analyze_fn_call_generic_arg(ira, fn_proto_node, arg_tuple_arg, &impl_fn->child_scope,
|
|
&next_proto_i, generic_id, &inst_fn_type_id, casted_args, impl_fn))
|
|
{
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
} else {
|
|
AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(next_proto_i);
|
|
assert(param_decl_node->type == NodeTypeParamDecl);
|
|
bool is_var_args = param_decl_node->data.param_decl.is_var_args;
|
|
if (is_var_args && !found_first_var_arg) {
|
|
first_var_arg = inst_fn_type_id.param_count;
|
|
found_first_var_arg = true;
|
|
}
|
|
|
|
if (!ir_analyze_fn_call_generic_arg(ira, fn_proto_node, arg, &impl_fn->child_scope,
|
|
&next_proto_i, generic_id, &inst_fn_type_id, casted_args, impl_fn))
|
|
{
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fn_proto_node->data.fn_proto.is_var_args) {
|
|
AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(next_proto_i);
|
|
Buf *param_name = param_decl_node->data.param_decl.name;
|
|
|
|
if (!found_first_var_arg) {
|
|
first_var_arg = inst_fn_type_id.param_count;
|
|
}
|
|
|
|
ConstExprValue *var_args_val = create_const_arg_tuple(ira->codegen,
|
|
first_var_arg, inst_fn_type_id.param_count);
|
|
ZigVar *var = add_variable(ira->codegen, param_decl_node,
|
|
impl_fn->child_scope, param_name, true, var_args_val, nullptr, var_args_val->type);
|
|
impl_fn->child_scope = var->child_scope;
|
|
}
|
|
|
|
if (fn_proto_node->data.fn_proto.align_expr != nullptr) {
|
|
ConstExprValue *align_result = ir_eval_const_value(ira->codegen, impl_fn->child_scope,
|
|
fn_proto_node->data.fn_proto.align_expr, get_align_amt_type(ira->codegen),
|
|
ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota,
|
|
nullptr, nullptr, fn_proto_node->data.fn_proto.align_expr, nullptr, ira->new_irb.exec, nullptr);
|
|
IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb,
|
|
impl_fn->child_scope, fn_proto_node->data.fn_proto.align_expr);
|
|
copy_const_val(&const_instruction->base.value, align_result, true);
|
|
|
|
uint32_t align_bytes = 0;
|
|
ir_resolve_align(ira, &const_instruction->base, &align_bytes);
|
|
impl_fn->align_bytes = align_bytes;
|
|
inst_fn_type_id.alignment = align_bytes;
|
|
}
|
|
|
|
if (fn_proto_node->data.fn_proto.return_var_token == nullptr) {
|
|
AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type;
|
|
ZigType *specified_return_type = ir_analyze_type_expr(ira, impl_fn->child_scope, return_type_node);
|
|
if (type_is_invalid(specified_return_type))
|
|
return ira->codegen->invalid_instruction;
|
|
if (fn_proto_node->data.fn_proto.auto_err_set) {
|
|
ZigType *inferred_err_set_type = get_auto_err_set_type(ira->codegen, impl_fn);
|
|
if ((err = type_resolve(ira->codegen, specified_return_type, ResolveStatusSizeKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
inst_fn_type_id.return_type = get_error_union_type(ira->codegen, inferred_err_set_type, specified_return_type);
|
|
} else {
|
|
inst_fn_type_id.return_type = specified_return_type;
|
|
}
|
|
|
|
switch (type_requires_comptime(ira->codegen, specified_return_type)) {
|
|
case ReqCompTimeYes:
|
|
// Throw out our work and call the function as if it were comptime.
|
|
return ir_analyze_fn_call(ira, call_instruction, fn_entry, fn_type, fn_ref, first_arg_ptr,
|
|
true, FnInlineAuto);
|
|
case ReqCompTimeInvalid:
|
|
return ira->codegen->invalid_instruction;
|
|
case ReqCompTimeNo:
|
|
break;
|
|
}
|
|
}
|
|
|
|
auto existing_entry = ira->codegen->generic_table.put_unique(generic_id, impl_fn);
|
|
if (existing_entry) {
|
|
// throw away all our work and use the existing function
|
|
impl_fn = existing_entry->value;
|
|
} else {
|
|
// finish instantiating the function
|
|
impl_fn->type_entry = get_fn_type(ira->codegen, &inst_fn_type_id);
|
|
if (type_is_invalid(impl_fn->type_entry))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
impl_fn->ir_executable.source_node = call_instruction->base.source_node;
|
|
impl_fn->ir_executable.parent_exec = ira->new_irb.exec;
|
|
impl_fn->analyzed_executable.source_node = call_instruction->base.source_node;
|
|
impl_fn->analyzed_executable.parent_exec = ira->new_irb.exec;
|
|
impl_fn->analyzed_executable.backward_branch_quota = ira->new_irb.exec->backward_branch_quota;
|
|
impl_fn->analyzed_executable.is_generic_instantiation = true;
|
|
|
|
ira->codegen->fn_defs.append(impl_fn);
|
|
}
|
|
|
|
FnTypeId *impl_fn_type_id = &impl_fn->type_entry->data.fn.fn_type_id;
|
|
IrInstruction *result_loc;
|
|
if (handle_is_ptr(impl_fn_type_id->return_type)) {
|
|
result_loc = ir_resolve_result(ira, &call_instruction->base, call_instruction->result_loc,
|
|
impl_fn_type_id->return_type, nullptr, true, true, false);
|
|
if (result_loc != nullptr && (type_is_invalid(result_loc->value.type) ||
|
|
instr_is_unreachable(result_loc)))
|
|
{
|
|
return result_loc;
|
|
}
|
|
} else {
|
|
result_loc = nullptr;
|
|
}
|
|
|
|
if (fn_type_can_fail(impl_fn_type_id)) {
|
|
parent_fn_entry->calls_or_awaits_errorable_fn = true;
|
|
}
|
|
|
|
size_t impl_param_count = impl_fn_type_id->param_count;
|
|
if (call_instruction->is_async) {
|
|
IrInstruction *result = ir_analyze_async_call(ira, call_instruction, impl_fn, impl_fn->type_entry,
|
|
nullptr, casted_args, call_param_count, casted_new_stack);
|
|
return ir_finish_anal(ira, result);
|
|
}
|
|
|
|
if (impl_fn_type_id->cc == CallingConventionAsync && parent_fn_entry->inferred_async_node == nullptr) {
|
|
parent_fn_entry->inferred_async_node = fn_ref->source_node;
|
|
}
|
|
|
|
IrInstructionCallGen *new_call_instruction = ir_build_call_gen(ira, &call_instruction->base,
|
|
impl_fn, nullptr, impl_param_count, casted_args, fn_inline,
|
|
call_instruction->is_async, casted_new_stack, result_loc,
|
|
impl_fn_type_id->return_type);
|
|
|
|
parent_fn_entry->call_list.append(new_call_instruction);
|
|
|
|
return ir_finish_anal(ira, &new_call_instruction->base);
|
|
}
|
|
|
|
ZigFn *parent_fn_entry = exec_fn_entry(ira->new_irb.exec);
|
|
assert(fn_type_id->return_type != nullptr);
|
|
assert(parent_fn_entry != nullptr);
|
|
if (fn_type_can_fail(fn_type_id)) {
|
|
parent_fn_entry->calls_or_awaits_errorable_fn = true;
|
|
}
|
|
|
|
|
|
IrInstruction **casted_args = allocate<IrInstruction *>(call_param_count);
|
|
size_t next_arg_index = 0;
|
|
if (first_arg_ptr) {
|
|
assert(first_arg_ptr->value.type->id == ZigTypeIdPointer);
|
|
|
|
ZigType *param_type = fn_type_id->param_info[next_arg_index].type;
|
|
if (type_is_invalid(param_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *first_arg;
|
|
if (param_type->id == ZigTypeIdPointer &&
|
|
handle_is_ptr(first_arg_ptr->value.type->data.pointer.child_type))
|
|
{
|
|
first_arg = first_arg_ptr;
|
|
} else {
|
|
first_arg = ir_get_deref(ira, first_arg_ptr, first_arg_ptr, nullptr);
|
|
if (type_is_invalid(first_arg->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *casted_arg = ir_implicit_cast(ira, first_arg, param_type);
|
|
if (type_is_invalid(casted_arg->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
casted_args[next_arg_index] = casted_arg;
|
|
next_arg_index += 1;
|
|
}
|
|
for (size_t call_i = 0; call_i < call_instruction->arg_count; call_i += 1) {
|
|
IrInstruction *old_arg = call_instruction->args[call_i]->child;
|
|
if (type_is_invalid(old_arg->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
IrInstruction *casted_arg;
|
|
if (next_arg_index < src_param_count) {
|
|
ZigType *param_type = fn_type_id->param_info[next_arg_index].type;
|
|
if (type_is_invalid(param_type))
|
|
return ira->codegen->invalid_instruction;
|
|
casted_arg = ir_implicit_cast(ira, old_arg, param_type);
|
|
if (type_is_invalid(casted_arg->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
} else {
|
|
casted_arg = old_arg;
|
|
}
|
|
|
|
casted_args[next_arg_index] = casted_arg;
|
|
next_arg_index += 1;
|
|
}
|
|
|
|
assert(next_arg_index == call_param_count);
|
|
|
|
ZigType *return_type = fn_type_id->return_type;
|
|
if (type_is_invalid(return_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (fn_entry != nullptr && fn_entry->fn_inline == FnInlineAlways && fn_inline == FnInlineNever) {
|
|
ir_add_error(ira, &call_instruction->base,
|
|
buf_sprintf("no-inline call of inline function"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (call_instruction->is_async) {
|
|
IrInstruction *result = ir_analyze_async_call(ira, call_instruction, fn_entry, fn_type, fn_ref,
|
|
casted_args, call_param_count, casted_new_stack);
|
|
return ir_finish_anal(ira, result);
|
|
}
|
|
|
|
if (fn_type_id->cc == CallingConventionAsync && parent_fn_entry->inferred_async_node == nullptr) {
|
|
parent_fn_entry->inferred_async_node = fn_ref->source_node;
|
|
}
|
|
|
|
IrInstruction *result_loc;
|
|
if (handle_is_ptr(return_type)) {
|
|
result_loc = ir_resolve_result(ira, &call_instruction->base, call_instruction->result_loc,
|
|
return_type, nullptr, true, true, false);
|
|
if (result_loc != nullptr && (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc))) {
|
|
return result_loc;
|
|
}
|
|
} else {
|
|
result_loc = nullptr;
|
|
}
|
|
|
|
IrInstructionCallGen *new_call_instruction = ir_build_call_gen(ira, &call_instruction->base, fn_entry, fn_ref,
|
|
call_param_count, casted_args, fn_inline, false, casted_new_stack,
|
|
result_loc, return_type);
|
|
parent_fn_entry->call_list.append(new_call_instruction);
|
|
return ir_finish_anal(ira, &new_call_instruction->base);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_call(IrAnalyze *ira, IrInstructionCallSrc *call_instruction) {
|
|
IrInstruction *fn_ref = call_instruction->fn_ref->child;
|
|
if (type_is_invalid(fn_ref->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
bool is_comptime = call_instruction->is_comptime ||
|
|
ir_should_inline(ira->new_irb.exec, call_instruction->base.scope);
|
|
|
|
if (is_comptime || instr_is_comptime(fn_ref)) {
|
|
if (fn_ref->value.type->id == ZigTypeIdMetaType) {
|
|
ZigType *dest_type = ir_resolve_type(ira, fn_ref);
|
|
if (type_is_invalid(dest_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
size_t actual_param_count = call_instruction->arg_count;
|
|
|
|
if (actual_param_count != 1) {
|
|
ir_add_error_node(ira, call_instruction->base.source_node,
|
|
buf_sprintf("cast expression expects exactly one parameter"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *arg = call_instruction->args[0]->child;
|
|
|
|
IrInstruction *cast_instruction = ir_analyze_cast(ira, &call_instruction->base, dest_type, arg,
|
|
call_instruction->result_loc);
|
|
if (type_is_invalid(cast_instruction->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
return ir_finish_anal(ira, cast_instruction);
|
|
} else if (fn_ref->value.type->id == ZigTypeIdFn) {
|
|
ZigFn *fn_table_entry = ir_resolve_fn(ira, fn_ref);
|
|
if (fn_table_entry == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
return ir_analyze_fn_call(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry,
|
|
fn_ref, nullptr, is_comptime, call_instruction->fn_inline);
|
|
} else if (fn_ref->value.type->id == ZigTypeIdBoundFn) {
|
|
assert(fn_ref->value.special == ConstValSpecialStatic);
|
|
ZigFn *fn_table_entry = fn_ref->value.data.x_bound_fn.fn;
|
|
IrInstruction *first_arg_ptr = fn_ref->value.data.x_bound_fn.first_arg;
|
|
return ir_analyze_fn_call(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry,
|
|
fn_ref, first_arg_ptr, is_comptime, call_instruction->fn_inline);
|
|
} else {
|
|
ir_add_error_node(ira, fn_ref->source_node,
|
|
buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->value.type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
|
|
if (fn_ref->value.type->id == ZigTypeIdFn) {
|
|
return ir_analyze_fn_call(ira, call_instruction, nullptr, fn_ref->value.type,
|
|
fn_ref, nullptr, false, FnInlineAuto);
|
|
} else {
|
|
ir_add_error_node(ira, fn_ref->source_node,
|
|
buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->value.type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
|
|
// out_val->type must be the type to read the pointer as
|
|
// if the type is different than the actual type then it does a comptime byte reinterpretation
|
|
static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node,
|
|
ConstExprValue *out_val, ConstExprValue *ptr_val)
|
|
{
|
|
Error err;
|
|
assert(out_val->type != nullptr);
|
|
|
|
ConstExprValue *pointee = const_ptr_pointee_unchecked(codegen, ptr_val);
|
|
|
|
if ((err = type_resolve(codegen, pointee->type, ResolveStatusSizeKnown)))
|
|
return ErrorSemanticAnalyzeFail;
|
|
if ((err = type_resolve(codegen, out_val->type, ResolveStatusSizeKnown)))
|
|
return ErrorSemanticAnalyzeFail;
|
|
|
|
size_t src_size = type_size(codegen, pointee->type);
|
|
size_t dst_size = type_size(codegen, out_val->type);
|
|
|
|
if (dst_size <= src_size) {
|
|
if (src_size == dst_size && types_have_same_zig_comptime_repr(pointee->type, out_val->type)) {
|
|
copy_const_val(out_val, pointee, ptr_val->data.x_ptr.mut != ConstPtrMutComptimeVar);
|
|
return ErrorNone;
|
|
}
|
|
Buf buf = BUF_INIT;
|
|
buf_resize(&buf, src_size);
|
|
buf_write_value_bytes(codegen, (uint8_t*)buf_ptr(&buf), pointee);
|
|
if ((err = buf_read_value_bytes(ira, codegen, source_node, (uint8_t*)buf_ptr(&buf), out_val)))
|
|
return err;
|
|
return ErrorNone;
|
|
}
|
|
|
|
switch (ptr_val->data.x_ptr.special) {
|
|
case ConstPtrSpecialInvalid:
|
|
zig_unreachable();
|
|
case ConstPtrSpecialNull:
|
|
if (dst_size == 0)
|
|
return ErrorNone;
|
|
opt_ir_add_error_node(ira, codegen, source_node,
|
|
buf_sprintf("attempt to read %" ZIG_PRI_usize " bytes from null pointer",
|
|
dst_size));
|
|
return ErrorSemanticAnalyzeFail;
|
|
case ConstPtrSpecialRef: {
|
|
opt_ir_add_error_node(ira, codegen, source_node,
|
|
buf_sprintf("attempt to read %" ZIG_PRI_usize " bytes from pointer to %s which is %" ZIG_PRI_usize " bytes",
|
|
dst_size, buf_ptr(&pointee->type->name), src_size));
|
|
return ErrorSemanticAnalyzeFail;
|
|
}
|
|
case ConstPtrSpecialBaseArray: {
|
|
ConstExprValue *array_val = ptr_val->data.x_ptr.data.base_array.array_val;
|
|
assert(array_val->type->id == ZigTypeIdArray);
|
|
if (array_val->data.x_array.special != ConstArraySpecialNone)
|
|
zig_panic("TODO");
|
|
size_t elem_size = src_size;
|
|
size_t elem_index = ptr_val->data.x_ptr.data.base_array.elem_index;
|
|
src_size = elem_size * (array_val->type->data.array.len - elem_index);
|
|
if (dst_size > src_size) {
|
|
opt_ir_add_error_node(ira, codegen, source_node,
|
|
buf_sprintf("attempt to read %" ZIG_PRI_usize " bytes from %s at index %" ZIG_PRI_usize " which is %" ZIG_PRI_usize " bytes",
|
|
dst_size, buf_ptr(&array_val->type->name), elem_index, src_size));
|
|
return ErrorSemanticAnalyzeFail;
|
|
}
|
|
size_t elem_count = (dst_size % elem_size == 0) ? (dst_size / elem_size) : (dst_size / elem_size + 1);
|
|
Buf buf = BUF_INIT;
|
|
buf_resize(&buf, elem_count * elem_size);
|
|
for (size_t i = 0; i < elem_count; i += 1) {
|
|
ConstExprValue *elem_val = &array_val->data.x_array.data.s_none.elements[elem_index + i];
|
|
buf_write_value_bytes(codegen, (uint8_t*)buf_ptr(&buf) + (i * elem_size), elem_val);
|
|
}
|
|
if ((err = buf_read_value_bytes(ira, codegen, source_node, (uint8_t*)buf_ptr(&buf), out_val)))
|
|
return err;
|
|
return ErrorNone;
|
|
}
|
|
case ConstPtrSpecialBaseStruct:
|
|
case ConstPtrSpecialBaseErrorUnionCode:
|
|
case ConstPtrSpecialBaseErrorUnionPayload:
|
|
case ConstPtrSpecialBaseOptionalPayload:
|
|
case ConstPtrSpecialDiscard:
|
|
case ConstPtrSpecialHardCodedAddr:
|
|
case ConstPtrSpecialFunction:
|
|
zig_panic("TODO");
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_optional_type(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) {
|
|
Error err;
|
|
IrInstruction *value = un_op_instruction->value->child;
|
|
ZigType *type_entry = ir_resolve_type(ira, value);
|
|
if (type_is_invalid(type_entry))
|
|
return ira->codegen->invalid_instruction;
|
|
if ((err = type_resolve(ira->codegen, type_entry, ResolveStatusSizeKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
switch (type_entry->id) {
|
|
case ZigTypeIdInvalid:
|
|
zig_unreachable();
|
|
case ZigTypeIdMetaType:
|
|
case ZigTypeIdVoid:
|
|
case ZigTypeIdBool:
|
|
case ZigTypeIdInt:
|
|
case ZigTypeIdVector:
|
|
case ZigTypeIdFloat:
|
|
case ZigTypeIdPointer:
|
|
case ZigTypeIdArray:
|
|
case ZigTypeIdStruct:
|
|
case ZigTypeIdComptimeFloat:
|
|
case ZigTypeIdComptimeInt:
|
|
case ZigTypeIdEnumLiteral:
|
|
case ZigTypeIdUndefined:
|
|
case ZigTypeIdNull:
|
|
case ZigTypeIdOptional:
|
|
case ZigTypeIdErrorUnion:
|
|
case ZigTypeIdErrorSet:
|
|
case ZigTypeIdEnum:
|
|
case ZigTypeIdUnion:
|
|
case ZigTypeIdFn:
|
|
case ZigTypeIdBoundFn:
|
|
case ZigTypeIdArgTuple:
|
|
case ZigTypeIdCoroFrame:
|
|
case ZigTypeIdAnyFrame:
|
|
return ir_const_type(ira, &un_op_instruction->base, get_optional_type(ira->codegen, type_entry));
|
|
|
|
case ZigTypeIdUnreachable:
|
|
case ZigTypeIdOpaque:
|
|
ir_add_error_node(ira, un_op_instruction->base.source_node,
|
|
buf_sprintf("type '%s' not optional", buf_ptr(&type_entry->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
static ErrorMsg *ir_eval_negation_scalar(IrAnalyze *ira, IrInstruction *source_instr, ZigType *scalar_type,
|
|
ConstExprValue *operand_val, ConstExprValue *scalar_out_val, bool is_wrap_op)
|
|
{
|
|
bool is_float = (scalar_type->id == ZigTypeIdFloat || scalar_type->id == ZigTypeIdComptimeFloat);
|
|
|
|
bool ok_type = ((scalar_type->id == ZigTypeIdInt && scalar_type->data.integral.is_signed) ||
|
|
scalar_type->id == ZigTypeIdComptimeInt || (is_float && !is_wrap_op));
|
|
|
|
if (!ok_type) {
|
|
const char *fmt = is_wrap_op ? "invalid wrapping negation type: '%s'" : "invalid negation type: '%s'";
|
|
return ir_add_error(ira, source_instr, buf_sprintf(fmt, buf_ptr(&scalar_type->name)));
|
|
}
|
|
|
|
if (is_float) {
|
|
float_negate(scalar_out_val, operand_val);
|
|
} else if (is_wrap_op) {
|
|
bigint_negate_wrap(&scalar_out_val->data.x_bigint, &operand_val->data.x_bigint,
|
|
scalar_type->data.integral.bit_count);
|
|
} else {
|
|
bigint_negate(&scalar_out_val->data.x_bigint, &operand_val->data.x_bigint);
|
|
}
|
|
|
|
scalar_out_val->type = scalar_type;
|
|
scalar_out_val->special = ConstValSpecialStatic;
|
|
|
|
if (is_wrap_op || is_float || scalar_type->id == ZigTypeIdComptimeInt) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (!bigint_fits_in_bits(&scalar_out_val->data.x_bigint, scalar_type->data.integral.bit_count, true)) {
|
|
return ir_add_error(ira, source_instr, buf_sprintf("negation caused overflow"));
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_negation(IrAnalyze *ira, IrInstructionUnOp *instruction) {
|
|
IrInstruction *value = instruction->value->child;
|
|
ZigType *expr_type = value->value.type;
|
|
if (type_is_invalid(expr_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
bool is_wrap_op = (instruction->op_id == IrUnOpNegationWrap);
|
|
|
|
ZigType *scalar_type = (expr_type->id == ZigTypeIdVector) ? expr_type->data.vector.elem_type : expr_type;
|
|
|
|
if (instr_is_comptime(value)) {
|
|
ConstExprValue *operand_val = ir_resolve_const(ira, value, UndefBad);
|
|
if (!operand_val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *result_instruction = ir_const(ira, &instruction->base, expr_type);
|
|
ConstExprValue *out_val = &result_instruction->value;
|
|
if (expr_type->id == ZigTypeIdVector) {
|
|
expand_undef_array(ira->codegen, operand_val);
|
|
out_val->special = ConstValSpecialUndef;
|
|
expand_undef_array(ira->codegen, out_val);
|
|
size_t len = expr_type->data.vector.len;
|
|
for (size_t i = 0; i < len; i += 1) {
|
|
ConstExprValue *scalar_operand_val = &operand_val->data.x_array.data.s_none.elements[i];
|
|
ConstExprValue *scalar_out_val = &out_val->data.x_array.data.s_none.elements[i];
|
|
assert(scalar_operand_val->type == scalar_type);
|
|
assert(scalar_out_val->type == scalar_type);
|
|
ErrorMsg *msg = ir_eval_negation_scalar(ira, &instruction->base, scalar_type,
|
|
scalar_operand_val, scalar_out_val, is_wrap_op);
|
|
if (msg != nullptr) {
|
|
add_error_note(ira->codegen, msg, instruction->base.source_node,
|
|
buf_sprintf("when computing vector element at index %" ZIG_PRI_usize, i));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
out_val->type = expr_type;
|
|
out_val->special = ConstValSpecialStatic;
|
|
} else {
|
|
if (ir_eval_negation_scalar(ira, &instruction->base, scalar_type, operand_val, out_val,
|
|
is_wrap_op) != nullptr)
|
|
{
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
return result_instruction;
|
|
}
|
|
|
|
IrInstruction *result = ir_build_un_op(&ira->new_irb,
|
|
instruction->base.scope, instruction->base.source_node,
|
|
instruction->op_id, value);
|
|
result->value.type = expr_type;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_bin_not(IrAnalyze *ira, IrInstructionUnOp *instruction) {
|
|
IrInstruction *value = instruction->value->child;
|
|
ZigType *expr_type = value->value.type;
|
|
if (type_is_invalid(expr_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (expr_type->id == ZigTypeIdInt) {
|
|
if (instr_is_comptime(value)) {
|
|
ConstExprValue *target_const_val = ir_resolve_const(ira, value, UndefBad);
|
|
if (target_const_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *result = ir_const(ira, &instruction->base, expr_type);
|
|
bigint_not(&result->value.data.x_bigint, &target_const_val->data.x_bigint,
|
|
expr_type->data.integral.bit_count, expr_type->data.integral.is_signed);
|
|
return result;
|
|
}
|
|
|
|
IrInstruction *result = ir_build_un_op(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node, IrUnOpBinNot, value);
|
|
result->value.type = expr_type;
|
|
return result;
|
|
}
|
|
|
|
ir_add_error(ira, &instruction->base,
|
|
buf_sprintf("unable to perform binary not operation on type '%s'", buf_ptr(&expr_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructionUnOp *instruction) {
|
|
IrUnOp op_id = instruction->op_id;
|
|
switch (op_id) {
|
|
case IrUnOpInvalid:
|
|
zig_unreachable();
|
|
case IrUnOpBinNot:
|
|
return ir_analyze_bin_not(ira, instruction);
|
|
case IrUnOpNegation:
|
|
case IrUnOpNegationWrap:
|
|
return ir_analyze_negation(ira, instruction);
|
|
case IrUnOpDereference: {
|
|
IrInstruction *ptr = instruction->value->child;
|
|
if (type_is_invalid(ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
ZigType *ptr_type = ptr->value.type;
|
|
if (ptr_type->id == ZigTypeIdPointer && ptr_type->data.pointer.ptr_len == PtrLenUnknown) {
|
|
ir_add_error_node(ira, instruction->base.source_node,
|
|
buf_sprintf("index syntax required for unknown-length pointer type '%s'",
|
|
buf_ptr(&ptr_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *result = ir_get_deref(ira, &instruction->base, ptr, instruction->result_loc);
|
|
if (result == ira->codegen->invalid_instruction)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
// If the result needs to be an lvalue, type check it
|
|
if (instruction->lval == LValPtr && result->value.type->id != ZigTypeIdPointer) {
|
|
ir_add_error(ira, &instruction->base,
|
|
buf_sprintf("attempt to dereference non-pointer type '%s'", buf_ptr(&result->value.type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
case IrUnOpOptional:
|
|
return ir_analyze_optional_type(ira, instruction);
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
static void ir_push_resume(IrAnalyze *ira, IrSuspendPosition pos) {
|
|
IrBasicBlock *old_bb = ira->old_irb.exec->basic_block_list.at(pos.basic_block_index);
|
|
if (old_bb->in_resume_stack) return;
|
|
ira->resume_stack.append(pos);
|
|
old_bb->in_resume_stack = true;
|
|
}
|
|
|
|
static void ir_push_resume_block(IrAnalyze *ira, IrBasicBlock *old_bb) {
|
|
if (ira->resume_stack.length != 0) {
|
|
ir_push_resume(ira, {old_bb->index, 0});
|
|
}
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_br(IrAnalyze *ira, IrInstructionBr *br_instruction) {
|
|
IrBasicBlock *old_dest_block = br_instruction->dest_block;
|
|
|
|
bool is_comptime;
|
|
if (!ir_resolve_comptime(ira, br_instruction->is_comptime->child, &is_comptime))
|
|
return ir_unreach_error(ira);
|
|
|
|
if (is_comptime || (old_dest_block->ref_count == 1 && old_dest_block->suspend_instruction_ref == nullptr))
|
|
return ir_inline_bb(ira, &br_instruction->base, old_dest_block);
|
|
|
|
IrBasicBlock *new_bb = ir_get_new_bb_runtime(ira, old_dest_block, &br_instruction->base);
|
|
if (new_bb == nullptr)
|
|
return ir_unreach_error(ira);
|
|
|
|
ir_push_resume_block(ira, old_dest_block);
|
|
|
|
IrInstruction *result = ir_build_br(&ira->new_irb,
|
|
br_instruction->base.scope, br_instruction->base.source_node, new_bb, nullptr);
|
|
result->value.type = ira->codegen->builtin_types.entry_unreachable;
|
|
return ir_finish_anal(ira, result);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstructionCondBr *cond_br_instruction) {
|
|
IrInstruction *condition = cond_br_instruction->condition->child;
|
|
if (type_is_invalid(condition->value.type))
|
|
return ir_unreach_error(ira);
|
|
|
|
bool is_comptime;
|
|
if (!ir_resolve_comptime(ira, cond_br_instruction->is_comptime->child, &is_comptime))
|
|
return ir_unreach_error(ira);
|
|
|
|
ZigType *bool_type = ira->codegen->builtin_types.entry_bool;
|
|
IrInstruction *casted_condition = ir_implicit_cast(ira, condition, bool_type);
|
|
if (type_is_invalid(casted_condition->value.type))
|
|
return ir_unreach_error(ira);
|
|
|
|
if (is_comptime || instr_is_comptime(casted_condition)) {
|
|
bool cond_is_true;
|
|
if (!ir_resolve_bool(ira, casted_condition, &cond_is_true))
|
|
return ir_unreach_error(ira);
|
|
|
|
IrBasicBlock *old_dest_block = cond_is_true ?
|
|
cond_br_instruction->then_block : cond_br_instruction->else_block;
|
|
|
|
if (is_comptime || (old_dest_block->ref_count == 1 && old_dest_block->suspend_instruction_ref == nullptr))
|
|
return ir_inline_bb(ira, &cond_br_instruction->base, old_dest_block);
|
|
|
|
IrBasicBlock *new_dest_block = ir_get_new_bb_runtime(ira, old_dest_block, &cond_br_instruction->base);
|
|
if (new_dest_block == nullptr)
|
|
return ir_unreach_error(ira);
|
|
|
|
ir_push_resume_block(ira, old_dest_block);
|
|
|
|
IrInstruction *result = ir_build_br(&ira->new_irb,
|
|
cond_br_instruction->base.scope, cond_br_instruction->base.source_node, new_dest_block, nullptr);
|
|
result->value.type = ira->codegen->builtin_types.entry_unreachable;
|
|
return ir_finish_anal(ira, result);
|
|
}
|
|
|
|
assert(cond_br_instruction->then_block != cond_br_instruction->else_block);
|
|
IrBasicBlock *new_then_block = ir_get_new_bb_runtime(ira, cond_br_instruction->then_block, &cond_br_instruction->base);
|
|
if (new_then_block == nullptr)
|
|
return ir_unreach_error(ira);
|
|
|
|
IrBasicBlock *new_else_block = ir_get_new_bb_runtime(ira, cond_br_instruction->else_block, &cond_br_instruction->base);
|
|
if (new_else_block == nullptr)
|
|
return ir_unreach_error(ira);
|
|
|
|
ir_push_resume_block(ira, cond_br_instruction->else_block);
|
|
ir_push_resume_block(ira, cond_br_instruction->then_block);
|
|
|
|
IrInstruction *result = ir_build_cond_br(&ira->new_irb,
|
|
cond_br_instruction->base.scope, cond_br_instruction->base.source_node,
|
|
casted_condition, new_then_block, new_else_block, nullptr);
|
|
result->value.type = ira->codegen->builtin_types.entry_unreachable;
|
|
return ir_finish_anal(ira, result);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_unreachable(IrAnalyze *ira,
|
|
IrInstructionUnreachable *unreachable_instruction)
|
|
{
|
|
IrInstruction *result = ir_build_unreachable(&ira->new_irb,
|
|
unreachable_instruction->base.scope, unreachable_instruction->base.source_node);
|
|
result->value.type = ira->codegen->builtin_types.entry_unreachable;
|
|
return ir_finish_anal(ira, result);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPhi *phi_instruction) {
|
|
if (ira->const_predecessor_bb) {
|
|
for (size_t i = 0; i < phi_instruction->incoming_count; i += 1) {
|
|
IrBasicBlock *predecessor = phi_instruction->incoming_blocks[i];
|
|
if (predecessor != ira->const_predecessor_bb)
|
|
continue;
|
|
IrInstruction *value = phi_instruction->incoming_values[i]->child;
|
|
assert(value->value.type);
|
|
if (type_is_invalid(value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (value->value.special != ConstValSpecialRuntime) {
|
|
IrInstruction *result = ir_const(ira, &phi_instruction->base, nullptr);
|
|
copy_const_val(&result->value, &value->value, true);
|
|
return result;
|
|
} else {
|
|
return value;
|
|
}
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
ResultLocPeerParent *peer_parent = phi_instruction->peer_parent;
|
|
if (peer_parent != nullptr && !peer_parent->skipped && !peer_parent->done_resuming &&
|
|
peer_parent->peers.length >= 2)
|
|
{
|
|
if (peer_parent->resolved_type == nullptr) {
|
|
IrInstruction **instructions = allocate<IrInstruction *>(peer_parent->peers.length);
|
|
for (size_t i = 0; i < peer_parent->peers.length; i += 1) {
|
|
ResultLocPeer *this_peer = peer_parent->peers.at(i);
|
|
|
|
IrInstruction *gen_instruction = this_peer->base.gen_instruction;
|
|
if (gen_instruction == nullptr) {
|
|
// unreachable instructions will cause implicit_elem_type to be null
|
|
if (this_peer->base.implicit_elem_type == nullptr) {
|
|
instructions[i] = ir_const_unreachable(ira, this_peer->base.source_instruction);
|
|
} else {
|
|
instructions[i] = ir_const(ira, this_peer->base.source_instruction,
|
|
this_peer->base.implicit_elem_type);
|
|
instructions[i]->value.special = ConstValSpecialRuntime;
|
|
}
|
|
} else {
|
|
instructions[i] = gen_instruction;
|
|
}
|
|
|
|
}
|
|
ZigType *expected_type = ir_result_loc_expected_type(ira, &phi_instruction->base, peer_parent->parent);
|
|
peer_parent->resolved_type = ir_resolve_peer_types(ira,
|
|
peer_parent->base.source_instruction->source_node, expected_type, instructions,
|
|
peer_parent->peers.length);
|
|
|
|
// the logic below assumes there are no instructions in the new current basic block yet
|
|
ir_assert(ira->new_irb.current_basic_block->instruction_list.length == 0, &phi_instruction->base);
|
|
|
|
// In case resolving the parent activates a suspend, do it now
|
|
IrInstruction *parent_result_loc = ir_resolve_result(ira, &phi_instruction->base, peer_parent->parent,
|
|
peer_parent->resolved_type, nullptr, false, false, true);
|
|
if (parent_result_loc != nullptr &&
|
|
(type_is_invalid(parent_result_loc->value.type) || instr_is_unreachable(parent_result_loc)))
|
|
{
|
|
return parent_result_loc;
|
|
}
|
|
// If the above code generated any instructions in the current basic block, we need
|
|
// to move them to the peer parent predecessor.
|
|
ZigList<IrInstruction *> instrs_to_move = {};
|
|
while (ira->new_irb.current_basic_block->instruction_list.length != 0) {
|
|
instrs_to_move.append(ira->new_irb.current_basic_block->instruction_list.pop());
|
|
}
|
|
if (instrs_to_move.length != 0) {
|
|
IrBasicBlock *predecessor = peer_parent->base.source_instruction->child->owner_bb;
|
|
IrInstruction *branch_instruction = predecessor->instruction_list.pop();
|
|
ir_assert(branch_instruction->value.type->id == ZigTypeIdUnreachable, &phi_instruction->base);
|
|
while (instrs_to_move.length != 0) {
|
|
predecessor->instruction_list.append(instrs_to_move.pop());
|
|
}
|
|
predecessor->instruction_list.append(branch_instruction);
|
|
}
|
|
}
|
|
|
|
IrSuspendPosition suspend_pos;
|
|
ira_suspend(ira, &phi_instruction->base, nullptr, &suspend_pos);
|
|
ir_push_resume(ira, suspend_pos);
|
|
|
|
for (size_t i = 0; i < peer_parent->peers.length; i += 1) {
|
|
ResultLocPeer *opposite_peer = peer_parent->peers.at(peer_parent->peers.length - i - 1);
|
|
if (opposite_peer->base.implicit_elem_type != nullptr &&
|
|
opposite_peer->base.implicit_elem_type->id != ZigTypeIdUnreachable)
|
|
{
|
|
ir_push_resume(ira, opposite_peer->suspend_pos);
|
|
}
|
|
}
|
|
|
|
peer_parent->done_resuming = true;
|
|
return ira_resume(ira);
|
|
}
|
|
|
|
ZigList<IrBasicBlock*> new_incoming_blocks = {0};
|
|
ZigList<IrInstruction*> new_incoming_values = {0};
|
|
|
|
for (size_t i = 0; i < phi_instruction->incoming_count; i += 1) {
|
|
IrBasicBlock *predecessor = phi_instruction->incoming_blocks[i];
|
|
if (predecessor->ref_count == 0)
|
|
continue;
|
|
|
|
|
|
IrInstruction *old_value = phi_instruction->incoming_values[i];
|
|
assert(old_value);
|
|
IrInstruction *new_value = old_value->child;
|
|
if (!new_value || new_value->value.type->id == ZigTypeIdUnreachable || predecessor->other == nullptr)
|
|
continue;
|
|
|
|
if (type_is_invalid(new_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
|
|
assert(predecessor->other);
|
|
new_incoming_blocks.append(predecessor->other);
|
|
new_incoming_values.append(new_value);
|
|
}
|
|
|
|
if (new_incoming_blocks.length == 0) {
|
|
IrInstruction *result = ir_build_unreachable(&ira->new_irb,
|
|
phi_instruction->base.scope, phi_instruction->base.source_node);
|
|
result->value.type = ira->codegen->builtin_types.entry_unreachable;
|
|
return ir_finish_anal(ira, result);
|
|
}
|
|
|
|
if (new_incoming_blocks.length == 1) {
|
|
return new_incoming_values.at(0);
|
|
}
|
|
|
|
ZigType *resolved_type = ir_resolve_peer_types(ira, phi_instruction->base.source_node, nullptr,
|
|
new_incoming_values.items, new_incoming_values.length);
|
|
if (type_is_invalid(resolved_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
switch (type_has_one_possible_value(ira->codegen, resolved_type)) {
|
|
case OnePossibleValueInvalid:
|
|
return ira->codegen->invalid_instruction;
|
|
case OnePossibleValueYes:
|
|
return ir_const(ira, &phi_instruction->base, resolved_type);
|
|
case OnePossibleValueNo:
|
|
break;
|
|
}
|
|
|
|
switch (type_requires_comptime(ira->codegen, resolved_type)) {
|
|
case ReqCompTimeInvalid:
|
|
return ira->codegen->invalid_instruction;
|
|
case ReqCompTimeYes:
|
|
ir_add_error_node(ira, phi_instruction->base.source_node,
|
|
buf_sprintf("values of type '%s' must be comptime known", buf_ptr(&resolved_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
case ReqCompTimeNo:
|
|
break;
|
|
}
|
|
|
|
bool all_stack_ptrs = (resolved_type->id == ZigTypeIdPointer);
|
|
|
|
// cast all values to the resolved type. however we can't put cast instructions in front of the phi instruction.
|
|
// so we go back and insert the casts as the last instruction in the corresponding predecessor blocks, and
|
|
// then make sure the branch instruction is preserved.
|
|
IrBasicBlock *cur_bb = ira->new_irb.current_basic_block;
|
|
for (size_t i = 0; i < new_incoming_values.length; i += 1) {
|
|
IrInstruction *new_value = new_incoming_values.at(i);
|
|
IrBasicBlock *predecessor = new_incoming_blocks.at(i);
|
|
ir_assert(predecessor->instruction_list.length != 0, &phi_instruction->base);
|
|
IrInstruction *branch_instruction = predecessor->instruction_list.pop();
|
|
ir_set_cursor_at_end(&ira->new_irb, predecessor);
|
|
IrInstruction *casted_value = ir_implicit_cast(ira, new_value, resolved_type);
|
|
if (type_is_invalid(casted_value->value.type)) {
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
new_incoming_values.items[i] = casted_value;
|
|
predecessor->instruction_list.append(branch_instruction);
|
|
|
|
if (all_stack_ptrs && (casted_value->value.special != ConstValSpecialRuntime ||
|
|
casted_value->value.data.rh_ptr != RuntimeHintPtrStack))
|
|
{
|
|
all_stack_ptrs = false;
|
|
}
|
|
}
|
|
ir_set_cursor_at_end(&ira->new_irb, cur_bb);
|
|
|
|
IrInstruction *result = ir_build_phi(&ira->new_irb,
|
|
phi_instruction->base.scope, phi_instruction->base.source_node,
|
|
new_incoming_blocks.length, new_incoming_blocks.items, new_incoming_values.items, nullptr);
|
|
result->value.type = resolved_type;
|
|
|
|
if (all_stack_ptrs) {
|
|
assert(result->value.special == ConstValSpecialRuntime);
|
|
result->value.data.rh_ptr = RuntimeHintPtrStack;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_var_ptr(IrAnalyze *ira, IrInstructionVarPtr *instruction) {
|
|
ZigVar *var = instruction->var;
|
|
IrInstruction *result = ir_get_var_ptr(ira, &instruction->base, var);
|
|
if (instruction->crossed_fndef_scope != nullptr && !instr_is_comptime(result)) {
|
|
ErrorMsg *msg = ir_add_error(ira, &instruction->base,
|
|
buf_sprintf("'%s' not accessible from inner function", buf_ptr(&var->name)));
|
|
add_error_note(ira->codegen, msg, instruction->crossed_fndef_scope->base.source_node,
|
|
buf_sprintf("crossed function definition here"));
|
|
add_error_note(ira->codegen, msg, var->decl_node,
|
|
buf_sprintf("declared here"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_align) {
|
|
assert(ptr_type->id == ZigTypeIdPointer);
|
|
return get_pointer_to_type_extra(g,
|
|
ptr_type->data.pointer.child_type,
|
|
ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile,
|
|
ptr_type->data.pointer.ptr_len,
|
|
new_align,
|
|
ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes,
|
|
ptr_type->data.pointer.allow_zero);
|
|
}
|
|
|
|
static ZigType *adjust_slice_align(CodeGen *g, ZigType *slice_type, uint32_t new_align) {
|
|
assert(is_slice(slice_type));
|
|
ZigType *ptr_type = adjust_ptr_align(g, slice_type->data.structure.fields[slice_ptr_index].type_entry,
|
|
new_align);
|
|
return get_slice_type(g, ptr_type);
|
|
}
|
|
|
|
static ZigType *adjust_ptr_len(CodeGen *g, ZigType *ptr_type, PtrLen ptr_len) {
|
|
assert(ptr_type->id == ZigTypeIdPointer);
|
|
return get_pointer_to_type_extra(g,
|
|
ptr_type->data.pointer.child_type,
|
|
ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile,
|
|
ptr_len,
|
|
ptr_type->data.pointer.explicit_alignment,
|
|
ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes,
|
|
ptr_type->data.pointer.allow_zero);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstructionElemPtr *elem_ptr_instruction) {
|
|
Error err;
|
|
IrInstruction *array_ptr = elem_ptr_instruction->array_ptr->child;
|
|
if (type_is_invalid(array_ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ConstExprValue *orig_array_ptr_val = &array_ptr->value;
|
|
|
|
IrInstruction *elem_index = elem_ptr_instruction->elem_index->child;
|
|
if (type_is_invalid(elem_index->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *ptr_type = orig_array_ptr_val->type;
|
|
assert(ptr_type->id == ZigTypeIdPointer);
|
|
|
|
ZigType *array_type = ptr_type->data.pointer.child_type;
|
|
|
|
// At first return_type will be the pointer type we want to return, except with an optimistic alignment.
|
|
// We will adjust return_type's alignment before returning it.
|
|
ZigType *return_type;
|
|
|
|
if (type_is_invalid(array_type)) {
|
|
return ira->codegen->invalid_instruction;
|
|
} else if (array_type->id == ZigTypeIdArray ||
|
|
(array_type->id == ZigTypeIdPointer &&
|
|
array_type->data.pointer.ptr_len == PtrLenSingle &&
|
|
array_type->data.pointer.child_type->id == ZigTypeIdArray))
|
|
{
|
|
if (array_type->id == ZigTypeIdPointer) {
|
|
array_type = array_type->data.pointer.child_type;
|
|
ptr_type = ptr_type->data.pointer.child_type;
|
|
if (orig_array_ptr_val->special != ConstValSpecialRuntime) {
|
|
orig_array_ptr_val = const_ptr_pointee(ira, ira->codegen, orig_array_ptr_val,
|
|
elem_ptr_instruction->base.source_node);
|
|
if (orig_array_ptr_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
if (array_type->data.array.len == 0) {
|
|
ir_add_error_node(ira, elem_ptr_instruction->base.source_node,
|
|
buf_sprintf("index 0 outside array of size 0"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
ZigType *child_type = array_type->data.array.child_type;
|
|
if (ptr_type->data.pointer.host_int_bytes == 0) {
|
|
return_type = get_pointer_to_type_extra(ira->codegen, child_type,
|
|
ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile,
|
|
elem_ptr_instruction->ptr_len,
|
|
ptr_type->data.pointer.explicit_alignment, 0, 0, false);
|
|
} else {
|
|
uint64_t elem_val_scalar;
|
|
if (!ir_resolve_usize(ira, elem_index, &elem_val_scalar))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
size_t bit_width = type_size_bits(ira->codegen, child_type);
|
|
size_t bit_offset = bit_width * elem_val_scalar;
|
|
|
|
return_type = get_pointer_to_type_extra(ira->codegen, child_type,
|
|
ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile,
|
|
elem_ptr_instruction->ptr_len,
|
|
1, (uint32_t)bit_offset, ptr_type->data.pointer.host_int_bytes, false);
|
|
}
|
|
} else if (array_type->id == ZigTypeIdPointer) {
|
|
if (array_type->data.pointer.ptr_len == PtrLenSingle) {
|
|
ir_add_error_node(ira, elem_ptr_instruction->base.source_node,
|
|
buf_sprintf("index of single-item pointer"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
return_type = adjust_ptr_len(ira->codegen, array_type, elem_ptr_instruction->ptr_len);
|
|
} else if (is_slice(array_type)) {
|
|
return_type = adjust_ptr_len(ira->codegen, array_type->data.structure.fields[slice_ptr_index].type_entry,
|
|
elem_ptr_instruction->ptr_len);
|
|
} else if (array_type->id == ZigTypeIdArgTuple) {
|
|
ConstExprValue *ptr_val = ir_resolve_const(ira, array_ptr, UndefBad);
|
|
if (!ptr_val)
|
|
return ira->codegen->invalid_instruction;
|
|
ConstExprValue *args_val = const_ptr_pointee(ira, ira->codegen, ptr_val, elem_ptr_instruction->base.source_node);
|
|
if (args_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
size_t start = args_val->data.x_arg_tuple.start_index;
|
|
size_t end = args_val->data.x_arg_tuple.end_index;
|
|
uint64_t elem_index_val;
|
|
if (!ir_resolve_usize(ira, elem_index, &elem_index_val))
|
|
return ira->codegen->invalid_instruction;
|
|
size_t index = elem_index_val;
|
|
size_t len = end - start;
|
|
if (index >= len) {
|
|
ir_add_error(ira, &elem_ptr_instruction->base,
|
|
buf_sprintf("index %" ZIG_PRI_usize " outside argument list of size %" ZIG_PRI_usize "", index, len));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
size_t abs_index = start + index;
|
|
ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec);
|
|
assert(fn_entry);
|
|
ZigVar *var = get_fn_var_by_index(fn_entry, abs_index);
|
|
bool is_const = true;
|
|
bool is_volatile = false;
|
|
if (var) {
|
|
return ir_get_var_ptr(ira, &elem_ptr_instruction->base, var);
|
|
} else {
|
|
return ir_get_const_ptr(ira, &elem_ptr_instruction->base, &ira->codegen->const_void_val,
|
|
ira->codegen->builtin_types.entry_void, ConstPtrMutComptimeConst, is_const, is_volatile, 0);
|
|
}
|
|
} else {
|
|
ir_add_error_node(ira, elem_ptr_instruction->base.source_node,
|
|
buf_sprintf("array access of non-array type '%s'", buf_ptr(&array_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
ZigType *usize = ira->codegen->builtin_types.entry_usize;
|
|
IrInstruction *casted_elem_index = ir_implicit_cast(ira, elem_index, usize);
|
|
if (casted_elem_index == ira->codegen->invalid_instruction)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
bool safety_check_on = elem_ptr_instruction->safety_check_on;
|
|
if ((err = ensure_complete_type(ira->codegen, return_type->data.pointer.child_type)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
uint64_t elem_size = type_size(ira->codegen, return_type->data.pointer.child_type);
|
|
uint64_t abi_align = get_abi_alignment(ira->codegen, return_type->data.pointer.child_type);
|
|
uint64_t ptr_align = get_ptr_align(ira->codegen, return_type);
|
|
if (instr_is_comptime(casted_elem_index)) {
|
|
uint64_t index = bigint_as_unsigned(&casted_elem_index->value.data.x_bigint);
|
|
if (array_type->id == ZigTypeIdArray) {
|
|
uint64_t array_len = array_type->data.array.len;
|
|
if (index >= array_len) {
|
|
ir_add_error_node(ira, elem_ptr_instruction->base.source_node,
|
|
buf_sprintf("index %" ZIG_PRI_u64 " outside array of size %" ZIG_PRI_u64,
|
|
index, array_len));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
safety_check_on = false;
|
|
}
|
|
|
|
{
|
|
// figure out the largest alignment possible
|
|
uint64_t chosen_align = abi_align;
|
|
if (ptr_align >= abi_align) {
|
|
while (ptr_align > abi_align) {
|
|
if ((index * elem_size) % ptr_align == 0) {
|
|
chosen_align = ptr_align;
|
|
break;
|
|
}
|
|
ptr_align >>= 1;
|
|
}
|
|
} else if (elem_size >= ptr_align && elem_size % ptr_align == 0) {
|
|
chosen_align = ptr_align;
|
|
} else {
|
|
// can't get here because guaranteed elem_size >= abi_align
|
|
zig_unreachable();
|
|
}
|
|
return_type = adjust_ptr_align(ira->codegen, return_type, chosen_align);
|
|
}
|
|
|
|
if (orig_array_ptr_val->special != ConstValSpecialRuntime &&
|
|
(orig_array_ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar ||
|
|
array_type->id == ZigTypeIdArray))
|
|
{
|
|
ConstExprValue *array_ptr_val = const_ptr_pointee(ira, ira->codegen, orig_array_ptr_val,
|
|
elem_ptr_instruction->base.source_node);
|
|
if (array_ptr_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (array_ptr_val->special == ConstValSpecialUndef && elem_ptr_instruction->init_array_type != nullptr) {
|
|
if (array_type->id == ZigTypeIdArray) {
|
|
array_ptr_val->data.x_array.special = ConstArraySpecialNone;
|
|
array_ptr_val->data.x_array.data.s_none.elements = create_const_vals(array_type->data.array.len);
|
|
array_ptr_val->special = ConstValSpecialStatic;
|
|
for (size_t i = 0; i < array_type->data.array.len; i += 1) {
|
|
ConstExprValue *elem_val = &array_ptr_val->data.x_array.data.s_none.elements[i];
|
|
elem_val->special = ConstValSpecialUndef;
|
|
elem_val->type = array_type->data.array.child_type;
|
|
elem_val->parent.id = ConstParentIdArray;
|
|
elem_val->parent.data.p_array.array_val = array_ptr_val;
|
|
elem_val->parent.data.p_array.elem_index = i;
|
|
}
|
|
} else if (is_slice(array_type)) {
|
|
ZigType *actual_array_type = ir_resolve_type(ira, elem_ptr_instruction->init_array_type->child);
|
|
if (type_is_invalid(actual_array_type))
|
|
return ira->codegen->invalid_instruction;
|
|
if (actual_array_type->id != ZigTypeIdArray) {
|
|
ir_add_error(ira, elem_ptr_instruction->init_array_type,
|
|
buf_sprintf("expected array type or [_], found slice"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
ConstExprValue *array_init_val = create_const_vals(1);
|
|
array_init_val->special = ConstValSpecialStatic;
|
|
array_init_val->type = actual_array_type;
|
|
array_init_val->data.x_array.special = ConstArraySpecialNone;
|
|
array_init_val->data.x_array.data.s_none.elements = create_const_vals(actual_array_type->data.array.len);
|
|
array_init_val->special = ConstValSpecialStatic;
|
|
for (size_t i = 0; i < actual_array_type->data.array.len; i += 1) {
|
|
ConstExprValue *elem_val = &array_init_val->data.x_array.data.s_none.elements[i];
|
|
elem_val->special = ConstValSpecialUndef;
|
|
elem_val->type = actual_array_type->data.array.child_type;
|
|
elem_val->parent.id = ConstParentIdArray;
|
|
elem_val->parent.data.p_array.array_val = array_init_val;
|
|
elem_val->parent.data.p_array.elem_index = i;
|
|
}
|
|
|
|
init_const_slice(ira->codegen, array_ptr_val, array_init_val, 0, actual_array_type->data.array.len,
|
|
false);
|
|
array_ptr_val->data.x_struct.fields[slice_ptr_index].data.x_ptr.mut = ConstPtrMutInfer;
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
|
|
if (array_ptr_val->special != ConstValSpecialRuntime &&
|
|
(array_type->id != ZigTypeIdPointer ||
|
|
array_ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr))
|
|
{
|
|
if (array_type->id == ZigTypeIdPointer) {
|
|
IrInstruction *result = ir_const(ira, &elem_ptr_instruction->base, return_type);
|
|
ConstExprValue *out_val = &result->value;
|
|
out_val->data.x_ptr.mut = array_ptr_val->data.x_ptr.mut;
|
|
size_t new_index;
|
|
size_t mem_size;
|
|
size_t old_size;
|
|
switch (array_ptr_val->data.x_ptr.special) {
|
|
case ConstPtrSpecialInvalid:
|
|
case ConstPtrSpecialDiscard:
|
|
zig_unreachable();
|
|
case ConstPtrSpecialRef:
|
|
mem_size = 1;
|
|
old_size = 1;
|
|
new_index = index;
|
|
|
|
out_val->data.x_ptr.special = ConstPtrSpecialRef;
|
|
out_val->data.x_ptr.data.ref.pointee = array_ptr_val->data.x_ptr.data.ref.pointee;
|
|
break;
|
|
case ConstPtrSpecialBaseArray:
|
|
{
|
|
size_t offset = array_ptr_val->data.x_ptr.data.base_array.elem_index;
|
|
new_index = offset + index;
|
|
mem_size = array_ptr_val->data.x_ptr.data.base_array.array_val->type->data.array.len;
|
|
old_size = mem_size - offset;
|
|
|
|
assert(array_ptr_val->data.x_ptr.data.base_array.array_val);
|
|
|
|
out_val->data.x_ptr.special = ConstPtrSpecialBaseArray;
|
|
out_val->data.x_ptr.data.base_array.array_val =
|
|
array_ptr_val->data.x_ptr.data.base_array.array_val;
|
|
out_val->data.x_ptr.data.base_array.elem_index = new_index;
|
|
out_val->data.x_ptr.data.base_array.is_cstr =
|
|
array_ptr_val->data.x_ptr.data.base_array.is_cstr;
|
|
|
|
break;
|
|
}
|
|
case ConstPtrSpecialBaseStruct:
|
|
zig_panic("TODO elem ptr on a const inner struct");
|
|
case ConstPtrSpecialBaseErrorUnionCode:
|
|
zig_panic("TODO elem ptr on a const inner error union code");
|
|
case ConstPtrSpecialBaseErrorUnionPayload:
|
|
zig_panic("TODO elem ptr on a const inner error union payload");
|
|
case ConstPtrSpecialBaseOptionalPayload:
|
|
zig_panic("TODO elem ptr on a const inner optional payload");
|
|
case ConstPtrSpecialHardCodedAddr:
|
|
zig_unreachable();
|
|
case ConstPtrSpecialFunction:
|
|
zig_panic("TODO element ptr of a function casted to a ptr");
|
|
case ConstPtrSpecialNull:
|
|
zig_panic("TODO elem ptr on a null pointer");
|
|
}
|
|
if (new_index >= mem_size) {
|
|
ir_add_error_node(ira, elem_ptr_instruction->base.source_node,
|
|
buf_sprintf("index %" ZIG_PRI_u64 " outside pointer of size %" ZIG_PRI_usize "", index, old_size));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
return result;
|
|
} else if (is_slice(array_type)) {
|
|
ConstExprValue *ptr_field = &array_ptr_val->data.x_struct.fields[slice_ptr_index];
|
|
ir_assert(ptr_field != nullptr, &elem_ptr_instruction->base);
|
|
if (ptr_field->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) {
|
|
IrInstruction *result = ir_build_elem_ptr(&ira->new_irb, elem_ptr_instruction->base.scope,
|
|
elem_ptr_instruction->base.source_node, array_ptr, casted_elem_index, false,
|
|
elem_ptr_instruction->ptr_len, nullptr);
|
|
result->value.type = return_type;
|
|
return result;
|
|
}
|
|
ConstExprValue *len_field = &array_ptr_val->data.x_struct.fields[slice_len_index];
|
|
IrInstruction *result = ir_const(ira, &elem_ptr_instruction->base, return_type);
|
|
ConstExprValue *out_val = &result->value;
|
|
uint64_t slice_len = bigint_as_unsigned(&len_field->data.x_bigint);
|
|
if (index >= slice_len) {
|
|
ir_add_error_node(ira, elem_ptr_instruction->base.source_node,
|
|
buf_sprintf("index %" ZIG_PRI_u64 " outside slice of size %" ZIG_PRI_u64,
|
|
index, slice_len));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
out_val->data.x_ptr.mut = ptr_field->data.x_ptr.mut;
|
|
switch (ptr_field->data.x_ptr.special) {
|
|
case ConstPtrSpecialInvalid:
|
|
case ConstPtrSpecialDiscard:
|
|
zig_unreachable();
|
|
case ConstPtrSpecialRef:
|
|
out_val->data.x_ptr.special = ConstPtrSpecialRef;
|
|
out_val->data.x_ptr.data.ref.pointee = ptr_field->data.x_ptr.data.ref.pointee;
|
|
break;
|
|
case ConstPtrSpecialBaseArray:
|
|
{
|
|
size_t offset = ptr_field->data.x_ptr.data.base_array.elem_index;
|
|
uint64_t new_index = offset + index;
|
|
assert(new_index < ptr_field->data.x_ptr.data.base_array.array_val->type->data.array.len);
|
|
out_val->data.x_ptr.special = ConstPtrSpecialBaseArray;
|
|
out_val->data.x_ptr.data.base_array.array_val =
|
|
ptr_field->data.x_ptr.data.base_array.array_val;
|
|
out_val->data.x_ptr.data.base_array.elem_index = new_index;
|
|
out_val->data.x_ptr.data.base_array.is_cstr =
|
|
ptr_field->data.x_ptr.data.base_array.is_cstr;
|
|
break;
|
|
}
|
|
case ConstPtrSpecialBaseStruct:
|
|
zig_panic("TODO elem ptr on a slice backed by const inner struct");
|
|
case ConstPtrSpecialBaseErrorUnionCode:
|
|
zig_panic("TODO elem ptr on a slice backed by const inner error union code");
|
|
case ConstPtrSpecialBaseErrorUnionPayload:
|
|
zig_panic("TODO elem ptr on a slice backed by const inner error union payload");
|
|
case ConstPtrSpecialBaseOptionalPayload:
|
|
zig_panic("TODO elem ptr on a slice backed by const optional payload");
|
|
case ConstPtrSpecialHardCodedAddr:
|
|
zig_unreachable();
|
|
case ConstPtrSpecialFunction:
|
|
zig_panic("TODO elem ptr on a slice that was ptrcast from a function");
|
|
case ConstPtrSpecialNull:
|
|
zig_panic("TODO elem ptr on a slice has a null pointer");
|
|
}
|
|
return result;
|
|
} else if (array_type->id == ZigTypeIdArray) {
|
|
IrInstruction *result;
|
|
if (orig_array_ptr_val->data.x_ptr.mut == ConstPtrMutInfer) {
|
|
result = ir_build_elem_ptr(&ira->new_irb, elem_ptr_instruction->base.scope,
|
|
elem_ptr_instruction->base.source_node, array_ptr, casted_elem_index,
|
|
false, elem_ptr_instruction->ptr_len, elem_ptr_instruction->init_array_type);
|
|
result->value.type = return_type;
|
|
result->value.special = ConstValSpecialStatic;
|
|
} else {
|
|
result = ir_const(ira, &elem_ptr_instruction->base, return_type);
|
|
}
|
|
ConstExprValue *out_val = &result->value;
|
|
out_val->data.x_ptr.special = ConstPtrSpecialBaseArray;
|
|
out_val->data.x_ptr.mut = orig_array_ptr_val->data.x_ptr.mut;
|
|
out_val->data.x_ptr.data.base_array.array_val = array_ptr_val;
|
|
out_val->data.x_ptr.data.base_array.elem_index = index;
|
|
return result;
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// runtime known element index
|
|
switch (type_requires_comptime(ira->codegen, return_type)) {
|
|
case ReqCompTimeYes:
|
|
ir_add_error(ira, elem_index,
|
|
buf_sprintf("values of type '%s' must be comptime known, but index value is runtime known",
|
|
buf_ptr(&return_type->data.pointer.child_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
case ReqCompTimeInvalid:
|
|
return ira->codegen->invalid_instruction;
|
|
case ReqCompTimeNo:
|
|
break;
|
|
}
|
|
if (ptr_align < abi_align) {
|
|
if (elem_size >= ptr_align && elem_size % ptr_align == 0) {
|
|
return_type = adjust_ptr_align(ira->codegen, return_type, ptr_align);
|
|
} else {
|
|
// can't get here because guaranteed elem_size >= abi_align
|
|
zig_unreachable();
|
|
}
|
|
} else {
|
|
return_type = adjust_ptr_align(ira->codegen, return_type, abi_align);
|
|
}
|
|
}
|
|
|
|
IrInstruction *result = ir_build_elem_ptr(&ira->new_irb, elem_ptr_instruction->base.scope,
|
|
elem_ptr_instruction->base.source_node, array_ptr, casted_elem_index, safety_check_on,
|
|
elem_ptr_instruction->ptr_len, elem_ptr_instruction->init_array_type);
|
|
result->value.type = return_type;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_container_member_access_inner(IrAnalyze *ira,
|
|
ZigType *bare_struct_type, Buf *field_name, IrInstruction *source_instr,
|
|
IrInstruction *container_ptr, ZigType *container_type)
|
|
{
|
|
if (!is_slice(bare_struct_type)) {
|
|
ScopeDecls *container_scope = get_container_scope(bare_struct_type);
|
|
assert(container_scope != nullptr);
|
|
auto entry = container_scope->decl_table.maybe_get(field_name);
|
|
Tld *tld = entry ? entry->value : nullptr;
|
|
if (tld && tld->id == TldIdFn) {
|
|
resolve_top_level_decl(ira->codegen, tld, source_instr->source_node);
|
|
if (tld->resolution == TldResolutionInvalid)
|
|
return ira->codegen->invalid_instruction;
|
|
TldFn *tld_fn = (TldFn *)tld;
|
|
ZigFn *fn_entry = tld_fn->fn_entry;
|
|
if (type_is_invalid(fn_entry->type_entry))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *bound_fn_value = ir_build_const_bound_fn(&ira->new_irb, source_instr->scope,
|
|
source_instr->source_node, fn_entry, container_ptr);
|
|
return ir_get_ref(ira, source_instr, bound_fn_value, true, false);
|
|
}
|
|
}
|
|
const char *prefix_name;
|
|
if (is_slice(bare_struct_type)) {
|
|
prefix_name = "";
|
|
} else if (bare_struct_type->id == ZigTypeIdStruct) {
|
|
prefix_name = "struct ";
|
|
} else if (bare_struct_type->id == ZigTypeIdEnum) {
|
|
prefix_name = "enum ";
|
|
} else if (bare_struct_type->id == ZigTypeIdUnion) {
|
|
prefix_name = "union ";
|
|
} else {
|
|
prefix_name = "";
|
|
}
|
|
ir_add_error_node(ira, source_instr->source_node,
|
|
buf_sprintf("no member named '%s' in %s'%s'", buf_ptr(field_name), prefix_name, buf_ptr(&bare_struct_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInstruction *source_instr,
|
|
TypeStructField *field, IrInstruction *struct_ptr, ZigType *struct_type, bool initializing)
|
|
{
|
|
switch (type_has_one_possible_value(ira->codegen, field->type_entry)) {
|
|
case OnePossibleValueInvalid:
|
|
return ira->codegen->invalid_instruction;
|
|
case OnePossibleValueYes: {
|
|
IrInstruction *elem = ir_const(ira, source_instr, field->type_entry);
|
|
return ir_get_ref(ira, source_instr, elem, false, false);
|
|
}
|
|
case OnePossibleValueNo:
|
|
break;
|
|
}
|
|
assert(struct_ptr->value.type->id == ZigTypeIdPointer);
|
|
bool is_packed = (struct_type->data.structure.layout == ContainerLayoutPacked);
|
|
uint32_t align_bytes = is_packed ? 1 : get_abi_alignment(ira->codegen, field->type_entry);
|
|
uint32_t ptr_bit_offset = struct_ptr->value.type->data.pointer.bit_offset_in_host;
|
|
uint32_t ptr_host_int_bytes = struct_ptr->value.type->data.pointer.host_int_bytes;
|
|
uint32_t host_int_bytes_for_result_type = (ptr_host_int_bytes == 0) ?
|
|
get_host_int_bytes(ira->codegen, struct_type, field) : ptr_host_int_bytes;
|
|
bool is_const = struct_ptr->value.type->data.pointer.is_const;
|
|
bool is_volatile = struct_ptr->value.type->data.pointer.is_volatile;
|
|
ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field->type_entry,
|
|
is_const, is_volatile, PtrLenSingle, align_bytes,
|
|
(uint32_t)(ptr_bit_offset + field->bit_offset_in_host),
|
|
(uint32_t)host_int_bytes_for_result_type, false);
|
|
if (instr_is_comptime(struct_ptr)) {
|
|
ConstExprValue *ptr_val = ir_resolve_const(ira, struct_ptr, UndefBad);
|
|
if (!ptr_val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) {
|
|
ConstExprValue *struct_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node);
|
|
if (struct_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
if (type_is_invalid(struct_val->type))
|
|
return ira->codegen->invalid_instruction;
|
|
if (initializing && struct_val->special == ConstValSpecialUndef) {
|
|
struct_val->data.x_struct.fields = create_const_vals(struct_type->data.structure.src_field_count);
|
|
struct_val->special = ConstValSpecialStatic;
|
|
for (size_t i = 0; i < struct_type->data.structure.src_field_count; i += 1) {
|
|
ConstExprValue *field_val = &struct_val->data.x_struct.fields[i];
|
|
field_val->special = ConstValSpecialUndef;
|
|
field_val->type = struct_type->data.structure.fields[i].type_entry;
|
|
field_val->parent.id = ConstParentIdStruct;
|
|
field_val->parent.data.p_struct.struct_val = struct_val;
|
|
field_val->parent.data.p_struct.field_index = i;
|
|
}
|
|
}
|
|
IrInstruction *result;
|
|
if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) {
|
|
result = ir_build_struct_field_ptr(&ira->new_irb, source_instr->scope,
|
|
source_instr->source_node, struct_ptr, field);
|
|
result->value.type = ptr_type;
|
|
result->value.special = ConstValSpecialStatic;
|
|
} else {
|
|
result = ir_const(ira, source_instr, ptr_type);
|
|
}
|
|
ConstExprValue *const_val = &result->value;
|
|
const_val->data.x_ptr.special = ConstPtrSpecialBaseStruct;
|
|
const_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut;
|
|
const_val->data.x_ptr.data.base_struct.struct_val = struct_val;
|
|
const_val->data.x_ptr.data.base_struct.field_index = field->src_index;
|
|
return result;
|
|
}
|
|
}
|
|
IrInstruction *result = ir_build_struct_field_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node,
|
|
struct_ptr, field);
|
|
result->value.type = ptr_type;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name,
|
|
IrInstruction *source_instr, IrInstruction *container_ptr, ZigType *container_type, bool initializing)
|
|
{
|
|
Error err;
|
|
|
|
ZigType *bare_type = container_ref_type(container_type);
|
|
if ((err = ensure_complete_type(ira->codegen, bare_type)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
assert(container_ptr->value.type->id == ZigTypeIdPointer);
|
|
if (bare_type->id == ZigTypeIdStruct) {
|
|
TypeStructField *field = find_struct_type_field(bare_type, field_name);
|
|
if (field != nullptr) {
|
|
return ir_analyze_struct_field_ptr(ira, source_instr, field, container_ptr, bare_type, initializing);
|
|
} else {
|
|
return ir_analyze_container_member_access_inner(ira, bare_type, field_name,
|
|
source_instr, container_ptr, container_type);
|
|
}
|
|
}
|
|
|
|
if (bare_type->id == ZigTypeIdEnum) {
|
|
return ir_analyze_container_member_access_inner(ira, bare_type, field_name,
|
|
source_instr, container_ptr, container_type);
|
|
}
|
|
|
|
if (bare_type->id == ZigTypeIdUnion) {
|
|
bool is_const = container_ptr->value.type->data.pointer.is_const;
|
|
bool is_volatile = container_ptr->value.type->data.pointer.is_volatile;
|
|
|
|
TypeUnionField *field = find_union_type_field(bare_type, field_name);
|
|
if (field == nullptr) {
|
|
return ir_analyze_container_member_access_inner(ira, bare_type, field_name,
|
|
source_instr, container_ptr, container_type);
|
|
}
|
|
ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field->type_entry,
|
|
is_const, is_volatile, PtrLenSingle, 0, 0, 0, false);
|
|
if (instr_is_comptime(container_ptr)) {
|
|
ConstExprValue *ptr_val = ir_resolve_const(ira, container_ptr, UndefBad);
|
|
if (!ptr_val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) {
|
|
ConstExprValue *union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node);
|
|
if (union_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
if (type_is_invalid(union_val->type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (initializing) {
|
|
ConstExprValue *payload_val = create_const_vals(1);
|
|
payload_val->special = ConstValSpecialUndef;
|
|
payload_val->type = field->type_entry;
|
|
payload_val->parent.id = ConstParentIdUnion;
|
|
payload_val->parent.data.p_union.union_val = union_val;
|
|
|
|
union_val->special = ConstValSpecialStatic;
|
|
bigint_init_bigint(&union_val->data.x_union.tag, &field->enum_field->value);
|
|
union_val->data.x_union.payload = payload_val;
|
|
} else {
|
|
TypeUnionField *actual_field = find_union_field_by_tag(bare_type, &union_val->data.x_union.tag);
|
|
if (actual_field == nullptr)
|
|
zig_unreachable();
|
|
|
|
if (field != actual_field) {
|
|
ir_add_error_node(ira, source_instr->source_node,
|
|
buf_sprintf("accessing union field '%s' while field '%s' is set", buf_ptr(field_name),
|
|
buf_ptr(actual_field->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
|
|
ConstExprValue *payload_val = union_val->data.x_union.payload;
|
|
|
|
|
|
IrInstruction *result;
|
|
if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) {
|
|
result = ir_build_union_field_ptr(&ira->new_irb, source_instr->scope,
|
|
source_instr->source_node, container_ptr, field, true, initializing);
|
|
result->value.type = ptr_type;
|
|
result->value.special = ConstValSpecialStatic;
|
|
} else {
|
|
result = ir_const(ira, source_instr, ptr_type);
|
|
}
|
|
ConstExprValue *const_val = &result->value;
|
|
const_val->data.x_ptr.special = ConstPtrSpecialRef;
|
|
const_val->data.x_ptr.mut = container_ptr->value.data.x_ptr.mut;
|
|
const_val->data.x_ptr.data.ref.pointee = payload_val;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
IrInstruction *result = ir_build_union_field_ptr(&ira->new_irb, source_instr->scope,
|
|
source_instr->source_node, container_ptr, field, true, initializing);
|
|
result->value.type = ptr_type;
|
|
return result;
|
|
}
|
|
|
|
zig_unreachable();
|
|
}
|
|
|
|
static void add_link_lib_symbol(IrAnalyze *ira, Buf *lib_name, Buf *symbol_name, AstNode *source_node) {
|
|
bool is_libc = target_is_libc_lib_name(ira->codegen->zig_target, buf_ptr(lib_name));
|
|
if (is_libc && ira->codegen->libc_link_lib == nullptr && !ira->codegen->reported_bad_link_libc_error) {
|
|
ir_add_error_node(ira, source_node,
|
|
buf_sprintf("dependency on library c must be explicitly specified in the build command"));
|
|
ira->codegen->reported_bad_link_libc_error = true;
|
|
}
|
|
|
|
LinkLib *link_lib = add_link_lib(ira->codegen, lib_name);
|
|
for (size_t i = 0; i < link_lib->symbols.length; i += 1) {
|
|
Buf *existing_symbol_name = link_lib->symbols.at(i);
|
|
if (buf_eql_buf(existing_symbol_name, symbol_name)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!is_libc && !target_is_wasm(ira->codegen->zig_target) && !ira->codegen->have_pic && !ira->codegen->reported_bad_link_libc_error) {
|
|
ErrorMsg *msg = ir_add_error_node(ira, source_node,
|
|
buf_sprintf("dependency on dynamic library '%s' requires enabling Position Independent Code",
|
|
buf_ptr(lib_name)));
|
|
add_error_note(ira->codegen, msg, source_node,
|
|
buf_sprintf("fixed by `--library %s` or `--enable-pic`", buf_ptr(lib_name)));
|
|
ira->codegen->reported_bad_link_libc_error = true;
|
|
}
|
|
|
|
for (size_t i = 0; i < ira->codegen->forbidden_libs.length; i += 1) {
|
|
Buf *forbidden_lib_name = ira->codegen->forbidden_libs.at(i);
|
|
if (buf_eql_buf(lib_name, forbidden_lib_name)) {
|
|
ir_add_error_node(ira, source_node,
|
|
buf_sprintf("linking against forbidden library '%s'", buf_ptr(symbol_name)));
|
|
}
|
|
}
|
|
link_lib->symbols.append(symbol_name);
|
|
}
|
|
|
|
static IrInstruction *ir_error_dependency_loop(IrAnalyze *ira, IrInstruction *source_instr) {
|
|
ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("dependency loop detected"));
|
|
emit_error_notes_for_ref_stack(ira->codegen, msg);
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source_instruction, Tld *tld) {
|
|
resolve_top_level_decl(ira->codegen, tld, source_instruction->source_node);
|
|
if (tld->resolution == TldResolutionInvalid)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
switch (tld->id) {
|
|
case TldIdContainer:
|
|
case TldIdCompTime:
|
|
case TldIdUsingNamespace:
|
|
zig_unreachable();
|
|
case TldIdVar:
|
|
{
|
|
TldVar *tld_var = (TldVar *)tld;
|
|
ZigVar *var = tld_var->var;
|
|
if (var == nullptr) {
|
|
return ir_error_dependency_loop(ira, source_instruction);
|
|
}
|
|
if (tld_var->extern_lib_name != nullptr) {
|
|
add_link_lib_symbol(ira, tld_var->extern_lib_name, &var->name, source_instruction->source_node);
|
|
}
|
|
|
|
return ir_get_var_ptr(ira, source_instruction, var);
|
|
}
|
|
case TldIdFn:
|
|
{
|
|
TldFn *tld_fn = (TldFn *)tld;
|
|
ZigFn *fn_entry = tld_fn->fn_entry;
|
|
assert(fn_entry->type_entry);
|
|
|
|
if (type_is_invalid(fn_entry->type_entry))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (tld_fn->extern_lib_name != nullptr) {
|
|
add_link_lib_symbol(ira, tld_fn->extern_lib_name, &fn_entry->symbol_name, source_instruction->source_node);
|
|
}
|
|
|
|
IrInstruction *fn_inst = ir_create_const_fn(&ira->new_irb, source_instruction->scope,
|
|
source_instruction->source_node, fn_entry);
|
|
return ir_get_ref(ira, source_instruction, fn_inst, true, false);
|
|
}
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
static ErrorTableEntry *find_err_table_entry(ZigType *err_set_type, Buf *field_name) {
|
|
assert(err_set_type->id == ZigTypeIdErrorSet);
|
|
for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) {
|
|
ErrorTableEntry *err_table_entry = err_set_type->data.error_set.errors[i];
|
|
if (buf_eql_buf(&err_table_entry->name, field_name)) {
|
|
return err_table_entry;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstructionFieldPtr *field_ptr_instruction) {
|
|
Error err;
|
|
IrInstruction *container_ptr = field_ptr_instruction->container_ptr->child;
|
|
if (type_is_invalid(container_ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *container_type = container_ptr->value.type->data.pointer.child_type;
|
|
|
|
Buf *field_name = field_ptr_instruction->field_name_buffer;
|
|
if (!field_name) {
|
|
IrInstruction *field_name_expr = field_ptr_instruction->field_name_expr->child;
|
|
field_name = ir_resolve_str(ira, field_name_expr);
|
|
if (!field_name)
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
|
|
AstNode *source_node = field_ptr_instruction->base.source_node;
|
|
|
|
if (type_is_invalid(container_type)) {
|
|
return ira->codegen->invalid_instruction;
|
|
} else if (is_slice(container_type) || is_container_ref(container_type)) {
|
|
assert(container_ptr->value.type->id == ZigTypeIdPointer);
|
|
if (container_type->id == ZigTypeIdPointer) {
|
|
ZigType *bare_type = container_ref_type(container_type);
|
|
IrInstruction *container_child = ir_get_deref(ira, &field_ptr_instruction->base, container_ptr, nullptr);
|
|
IrInstruction *result = ir_analyze_container_field_ptr(ira, field_name, &field_ptr_instruction->base, container_child, bare_type, field_ptr_instruction->initializing);
|
|
return result;
|
|
} else {
|
|
IrInstruction *result = ir_analyze_container_field_ptr(ira, field_name, &field_ptr_instruction->base, container_ptr, container_type, field_ptr_instruction->initializing);
|
|
return result;
|
|
}
|
|
} else if (is_array_ref(container_type) && !field_ptr_instruction->initializing) {
|
|
if (buf_eql_str(field_name, "len")) {
|
|
ConstExprValue *len_val = create_const_vals(1);
|
|
if (container_type->id == ZigTypeIdPointer) {
|
|
init_const_usize(ira->codegen, len_val, container_type->data.pointer.child_type->data.array.len);
|
|
} else {
|
|
init_const_usize(ira->codegen, len_val, container_type->data.array.len);
|
|
}
|
|
|
|
ZigType *usize = ira->codegen->builtin_types.entry_usize;
|
|
bool ptr_is_const = true;
|
|
bool ptr_is_volatile = false;
|
|
return ir_get_const_ptr(ira, &field_ptr_instruction->base, len_val,
|
|
usize, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
|
|
} else {
|
|
ir_add_error_node(ira, source_node,
|
|
buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name),
|
|
buf_ptr(&container_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
} else if (container_type->id == ZigTypeIdArgTuple) {
|
|
ConstExprValue *container_ptr_val = ir_resolve_const(ira, container_ptr, UndefBad);
|
|
if (!container_ptr_val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
assert(container_ptr->value.type->id == ZigTypeIdPointer);
|
|
ConstExprValue *child_val = const_ptr_pointee(ira, ira->codegen, container_ptr_val, source_node);
|
|
if (child_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (buf_eql_str(field_name, "len")) {
|
|
ConstExprValue *len_val = create_const_vals(1);
|
|
size_t len = child_val->data.x_arg_tuple.end_index - child_val->data.x_arg_tuple.start_index;
|
|
init_const_usize(ira->codegen, len_val, len);
|
|
|
|
ZigType *usize = ira->codegen->builtin_types.entry_usize;
|
|
bool ptr_is_const = true;
|
|
bool ptr_is_volatile = false;
|
|
return ir_get_const_ptr(ira, &field_ptr_instruction->base, len_val,
|
|
usize, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
|
|
} else {
|
|
ir_add_error_node(ira, source_node,
|
|
buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name),
|
|
buf_ptr(&container_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
} else if (container_type->id == ZigTypeIdMetaType) {
|
|
ConstExprValue *container_ptr_val = ir_resolve_const(ira, container_ptr, UndefBad);
|
|
if (!container_ptr_val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
assert(container_ptr->value.type->id == ZigTypeIdPointer);
|
|
ConstExprValue *child_val = const_ptr_pointee(ira, ira->codegen, container_ptr_val, source_node);
|
|
if (child_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
ZigType *child_type = child_val->data.x_type;
|
|
|
|
if (type_is_invalid(child_type)) {
|
|
return ira->codegen->invalid_instruction;
|
|
} else if (is_container(child_type)) {
|
|
if (child_type->id == ZigTypeIdEnum) {
|
|
if ((err = ensure_complete_type(ira->codegen, child_type)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
TypeEnumField *field = find_enum_type_field(child_type, field_name);
|
|
if (field) {
|
|
bool ptr_is_const = true;
|
|
bool ptr_is_volatile = false;
|
|
return ir_get_const_ptr(ira, &field_ptr_instruction->base,
|
|
create_const_enum(child_type, &field->value), child_type,
|
|
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
|
|
}
|
|
}
|
|
ScopeDecls *container_scope = get_container_scope(child_type);
|
|
Tld *tld = find_container_decl(ira->codegen, container_scope, field_name);
|
|
if (tld) {
|
|
if (tld->visib_mod == VisibModPrivate &&
|
|
tld->import != get_scope_import(field_ptr_instruction->base.scope))
|
|
{
|
|
ErrorMsg *msg = ir_add_error(ira, &field_ptr_instruction->base,
|
|
buf_sprintf("'%s' is private", buf_ptr(field_name)));
|
|
add_error_note(ira->codegen, msg, tld->source_node, buf_sprintf("declared here"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
return ir_analyze_decl_ref(ira, &field_ptr_instruction->base, tld);
|
|
}
|
|
if (child_type->id == ZigTypeIdUnion &&
|
|
(child_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr ||
|
|
child_type->data.unionation.decl_node->data.container_decl.auto_enum))
|
|
{
|
|
if ((err = ensure_complete_type(ira->codegen, child_type)))
|
|
return ira->codegen->invalid_instruction;
|
|
TypeUnionField *field = find_union_type_field(child_type, field_name);
|
|
if (field) {
|
|
ZigType *enum_type = child_type->data.unionation.tag_type;
|
|
bool ptr_is_const = true;
|
|
bool ptr_is_volatile = false;
|
|
return ir_get_const_ptr(ira, &field_ptr_instruction->base,
|
|
create_const_enum(enum_type, &field->enum_field->value), enum_type,
|
|
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
|
|
}
|
|
}
|
|
const char *container_name = (child_type == ira->codegen->root_import) ?
|
|
"root source file" : buf_ptr(buf_sprintf("container '%s'", buf_ptr(&child_type->name)));
|
|
ir_add_error(ira, &field_ptr_instruction->base,
|
|
buf_sprintf("%s has no member called '%s'",
|
|
container_name, buf_ptr(field_name)));
|
|
return ira->codegen->invalid_instruction;
|
|
} else if (child_type->id == ZigTypeIdErrorSet) {
|
|
ErrorTableEntry *err_entry;
|
|
ZigType *err_set_type;
|
|
if (type_is_global_error_set(child_type)) {
|
|
auto existing_entry = ira->codegen->error_table.maybe_get(field_name);
|
|
if (existing_entry) {
|
|
err_entry = existing_entry->value;
|
|
} else {
|
|
err_entry = allocate<ErrorTableEntry>(1);
|
|
err_entry->decl_node = field_ptr_instruction->base.source_node;
|
|
buf_init_from_buf(&err_entry->name, field_name);
|
|
size_t error_value_count = ira->codegen->errors_by_index.length;
|
|
assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)ira->codegen->err_tag_type->data.integral.bit_count));
|
|
err_entry->value = error_value_count;
|
|
ira->codegen->errors_by_index.append(err_entry);
|
|
ira->codegen->error_table.put(field_name, err_entry);
|
|
}
|
|
if (err_entry->set_with_only_this_in_it == nullptr) {
|
|
err_entry->set_with_only_this_in_it = make_err_set_with_one_item(ira->codegen,
|
|
field_ptr_instruction->base.scope, field_ptr_instruction->base.source_node,
|
|
err_entry);
|
|
}
|
|
err_set_type = err_entry->set_with_only_this_in_it;
|
|
} else {
|
|
if (!resolve_inferred_error_set(ira->codegen, child_type, field_ptr_instruction->base.source_node)) {
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
err_entry = find_err_table_entry(child_type, field_name);
|
|
if (err_entry == nullptr) {
|
|
ir_add_error(ira, &field_ptr_instruction->base,
|
|
buf_sprintf("no error named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&child_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
err_set_type = child_type;
|
|
}
|
|
ConstExprValue *const_val = create_const_vals(1);
|
|
const_val->special = ConstValSpecialStatic;
|
|
const_val->type = err_set_type;
|
|
const_val->data.x_err_set = err_entry;
|
|
|
|
bool ptr_is_const = true;
|
|
bool ptr_is_volatile = false;
|
|
return ir_get_const_ptr(ira, &field_ptr_instruction->base, const_val,
|
|
err_set_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
|
|
} else if (child_type->id == ZigTypeIdInt) {
|
|
if (buf_eql_str(field_name, "bit_count")) {
|
|
bool ptr_is_const = true;
|
|
bool ptr_is_volatile = false;
|
|
return ir_get_const_ptr(ira, &field_ptr_instruction->base,
|
|
create_const_unsigned_negative(ira->codegen->builtin_types.entry_num_lit_int,
|
|
child_type->data.integral.bit_count, false),
|
|
ira->codegen->builtin_types.entry_num_lit_int,
|
|
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
|
|
} else if (buf_eql_str(field_name, "is_signed")) {
|
|
bool ptr_is_const = true;
|
|
bool ptr_is_volatile = false;
|
|
return ir_get_const_ptr(ira, &field_ptr_instruction->base,
|
|
create_const_bool(ira->codegen, child_type->data.integral.is_signed),
|
|
ira->codegen->builtin_types.entry_bool,
|
|
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
|
|
} else {
|
|
ir_add_error(ira, &field_ptr_instruction->base,
|
|
buf_sprintf("type '%s' has no member called '%s'",
|
|
buf_ptr(&child_type->name), buf_ptr(field_name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
} else if (child_type->id == ZigTypeIdFloat) {
|
|
if (buf_eql_str(field_name, "bit_count")) {
|
|
bool ptr_is_const = true;
|
|
bool ptr_is_volatile = false;
|
|
return ir_get_const_ptr(ira, &field_ptr_instruction->base,
|
|
create_const_unsigned_negative(ira->codegen->builtin_types.entry_num_lit_int,
|
|
child_type->data.floating.bit_count, false),
|
|
ira->codegen->builtin_types.entry_num_lit_int,
|
|
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
|
|
} else {
|
|
ir_add_error(ira, &field_ptr_instruction->base,
|
|
buf_sprintf("type '%s' has no member called '%s'",
|
|
buf_ptr(&child_type->name), buf_ptr(field_name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
} else if (child_type->id == ZigTypeIdPointer) {
|
|
if (buf_eql_str(field_name, "Child")) {
|
|
bool ptr_is_const = true;
|
|
bool ptr_is_volatile = false;
|
|
return ir_get_const_ptr(ira, &field_ptr_instruction->base,
|
|
create_const_type(ira->codegen, child_type->data.pointer.child_type),
|
|
ira->codegen->builtin_types.entry_type,
|
|
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
|
|
} else if (buf_eql_str(field_name, "alignment")) {
|
|
bool ptr_is_const = true;
|
|
bool ptr_is_volatile = false;
|
|
if ((err = type_resolve(ira->codegen, child_type->data.pointer.child_type,
|
|
ResolveStatusAlignmentKnown)))
|
|
{
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
return ir_get_const_ptr(ira, &field_ptr_instruction->base,
|
|
create_const_unsigned_negative(ira->codegen->builtin_types.entry_num_lit_int,
|
|
get_ptr_align(ira->codegen, child_type), false),
|
|
ira->codegen->builtin_types.entry_num_lit_int,
|
|
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
|
|
} else {
|
|
ir_add_error(ira, &field_ptr_instruction->base,
|
|
buf_sprintf("type '%s' has no member called '%s'",
|
|
buf_ptr(&child_type->name), buf_ptr(field_name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
} else if (child_type->id == ZigTypeIdArray) {
|
|
if (buf_eql_str(field_name, "Child")) {
|
|
bool ptr_is_const = true;
|
|
bool ptr_is_volatile = false;
|
|
return ir_get_const_ptr(ira, &field_ptr_instruction->base,
|
|
create_const_type(ira->codegen, child_type->data.array.child_type),
|
|
ira->codegen->builtin_types.entry_type,
|
|
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
|
|
} else if (buf_eql_str(field_name, "len")) {
|
|
bool ptr_is_const = true;
|
|
bool ptr_is_volatile = false;
|
|
return ir_get_const_ptr(ira, &field_ptr_instruction->base,
|
|
create_const_unsigned_negative(ira->codegen->builtin_types.entry_num_lit_int,
|
|
child_type->data.array.len, false),
|
|
ira->codegen->builtin_types.entry_num_lit_int,
|
|
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
|
|
} else {
|
|
ir_add_error(ira, &field_ptr_instruction->base,
|
|
buf_sprintf("type '%s' has no member called '%s'",
|
|
buf_ptr(&child_type->name), buf_ptr(field_name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
} else if (child_type->id == ZigTypeIdErrorUnion) {
|
|
if (buf_eql_str(field_name, "Payload")) {
|
|
bool ptr_is_const = true;
|
|
bool ptr_is_volatile = false;
|
|
return ir_get_const_ptr(ira, &field_ptr_instruction->base,
|
|
create_const_type(ira->codegen, child_type->data.error_union.payload_type),
|
|
ira->codegen->builtin_types.entry_type,
|
|
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
|
|
} else if (buf_eql_str(field_name, "ErrorSet")) {
|
|
bool ptr_is_const = true;
|
|
bool ptr_is_volatile = false;
|
|
return ir_get_const_ptr(ira, &field_ptr_instruction->base,
|
|
create_const_type(ira->codegen, child_type->data.error_union.err_set_type),
|
|
ira->codegen->builtin_types.entry_type,
|
|
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
|
|
} else {
|
|
ir_add_error(ira, &field_ptr_instruction->base,
|
|
buf_sprintf("type '%s' has no member called '%s'",
|
|
buf_ptr(&child_type->name), buf_ptr(field_name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
} else if (child_type->id == ZigTypeIdOptional) {
|
|
if (buf_eql_str(field_name, "Child")) {
|
|
bool ptr_is_const = true;
|
|
bool ptr_is_volatile = false;
|
|
return ir_get_const_ptr(ira, &field_ptr_instruction->base,
|
|
create_const_type(ira->codegen, child_type->data.maybe.child_type),
|
|
ira->codegen->builtin_types.entry_type,
|
|
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
|
|
} else {
|
|
ir_add_error(ira, &field_ptr_instruction->base,
|
|
buf_sprintf("type '%s' has no member called '%s'",
|
|
buf_ptr(&child_type->name), buf_ptr(field_name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
} else if (child_type->id == ZigTypeIdFn) {
|
|
if (buf_eql_str(field_name, "ReturnType")) {
|
|
if (child_type->data.fn.fn_type_id.return_type == nullptr) {
|
|
// Return type can only ever be null, if the function is generic
|
|
assert(child_type->data.fn.is_generic);
|
|
|
|
ir_add_error(ira, &field_ptr_instruction->base,
|
|
buf_sprintf("ReturnType has not been resolved because '%s' is generic", buf_ptr(&child_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
bool ptr_is_const = true;
|
|
bool ptr_is_volatile = false;
|
|
return ir_get_const_ptr(ira, &field_ptr_instruction->base,
|
|
create_const_type(ira->codegen, child_type->data.fn.fn_type_id.return_type),
|
|
ira->codegen->builtin_types.entry_type,
|
|
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
|
|
} else if (buf_eql_str(field_name, "is_var_args")) {
|
|
bool ptr_is_const = true;
|
|
bool ptr_is_volatile = false;
|
|
return ir_get_const_ptr(ira, &field_ptr_instruction->base,
|
|
create_const_bool(ira->codegen, child_type->data.fn.fn_type_id.is_var_args),
|
|
ira->codegen->builtin_types.entry_bool,
|
|
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
|
|
} else if (buf_eql_str(field_name, "arg_count")) {
|
|
bool ptr_is_const = true;
|
|
bool ptr_is_volatile = false;
|
|
return ir_get_const_ptr(ira, &field_ptr_instruction->base,
|
|
create_const_usize(ira->codegen, child_type->data.fn.fn_type_id.param_count),
|
|
ira->codegen->builtin_types.entry_usize,
|
|
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
|
|
} else {
|
|
ir_add_error(ira, &field_ptr_instruction->base,
|
|
buf_sprintf("type '%s' has no member called '%s'",
|
|
buf_ptr(&child_type->name), buf_ptr(field_name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
} else {
|
|
ir_add_error(ira, &field_ptr_instruction->base,
|
|
buf_sprintf("type '%s' does not support field access", buf_ptr(&child_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
} else if (field_ptr_instruction->initializing) {
|
|
ir_add_error(ira, &field_ptr_instruction->base,
|
|
buf_sprintf("type '%s' does not support struct initialization syntax", buf_ptr(&container_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
} else {
|
|
ir_add_error_node(ira, field_ptr_instruction->base.source_node,
|
|
buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstructionStorePtr *instruction) {
|
|
IrInstruction *ptr = instruction->ptr->child;
|
|
if (type_is_invalid(ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *value = instruction->value->child;
|
|
if (type_is_invalid(value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
return ir_analyze_store_ptr(ira, &instruction->base, ptr, value, instruction->allow_write_through_const);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstructionLoadPtr *instruction) {
|
|
IrInstruction *ptr = instruction->ptr->child;
|
|
if (type_is_invalid(ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
return ir_get_deref(ira, &instruction->base, ptr, nullptr);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructionTypeOf *typeof_instruction) {
|
|
IrInstruction *expr_value = typeof_instruction->value->child;
|
|
ZigType *type_entry = expr_value->value.type;
|
|
if (type_is_invalid(type_entry))
|
|
return ira->codegen->invalid_instruction;
|
|
return ir_const_type(ira, &typeof_instruction->base, type_entry);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_set_cold(IrAnalyze *ira, IrInstructionSetCold *instruction) {
|
|
if (ira->new_irb.exec->is_inline) {
|
|
// ignore setCold when running functions at compile time
|
|
return ir_const_void(ira, &instruction->base);
|
|
}
|
|
|
|
IrInstruction *is_cold_value = instruction->is_cold->child;
|
|
bool want_cold;
|
|
if (!ir_resolve_bool(ira, is_cold_value, &want_cold))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigFn *fn_entry = scope_fn_entry(instruction->base.scope);
|
|
if (fn_entry == nullptr) {
|
|
ir_add_error(ira, &instruction->base, buf_sprintf("@setCold outside function"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (fn_entry->set_cold_node != nullptr) {
|
|
ErrorMsg *msg = ir_add_error(ira, &instruction->base, buf_sprintf("cold set twice in same function"));
|
|
add_error_note(ira->codegen, msg, fn_entry->set_cold_node, buf_sprintf("first set here"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
fn_entry->set_cold_node = instruction->base.source_node;
|
|
fn_entry->is_cold = want_cold;
|
|
|
|
return ir_const_void(ira, &instruction->base);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_set_runtime_safety(IrAnalyze *ira,
|
|
IrInstructionSetRuntimeSafety *set_runtime_safety_instruction)
|
|
{
|
|
if (ira->new_irb.exec->is_inline) {
|
|
// ignore setRuntimeSafety when running functions at compile time
|
|
return ir_const_void(ira, &set_runtime_safety_instruction->base);
|
|
}
|
|
|
|
bool *safety_off_ptr;
|
|
AstNode **safety_set_node_ptr;
|
|
|
|
Scope *scope = set_runtime_safety_instruction->base.scope;
|
|
while (scope != nullptr) {
|
|
if (scope->id == ScopeIdBlock) {
|
|
ScopeBlock *block_scope = (ScopeBlock *)scope;
|
|
safety_off_ptr = &block_scope->safety_off;
|
|
safety_set_node_ptr = &block_scope->safety_set_node;
|
|
break;
|
|
} else if (scope->id == ScopeIdFnDef) {
|
|
ScopeFnDef *def_scope = (ScopeFnDef *)scope;
|
|
ZigFn *target_fn = def_scope->fn_entry;
|
|
assert(target_fn->def_scope != nullptr);
|
|
safety_off_ptr = &target_fn->def_scope->safety_off;
|
|
safety_set_node_ptr = &target_fn->def_scope->safety_set_node;
|
|
break;
|
|
} else if (scope->id == ScopeIdDecls) {
|
|
ScopeDecls *decls_scope = (ScopeDecls *)scope;
|
|
safety_off_ptr = &decls_scope->safety_off;
|
|
safety_set_node_ptr = &decls_scope->safety_set_node;
|
|
break;
|
|
} else {
|
|
scope = scope->parent;
|
|
continue;
|
|
}
|
|
}
|
|
assert(scope != nullptr);
|
|
|
|
IrInstruction *safety_on_value = set_runtime_safety_instruction->safety_on->child;
|
|
bool want_runtime_safety;
|
|
if (!ir_resolve_bool(ira, safety_on_value, &want_runtime_safety))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
AstNode *source_node = set_runtime_safety_instruction->base.source_node;
|
|
if (*safety_set_node_ptr) {
|
|
ErrorMsg *msg = ir_add_error_node(ira, source_node,
|
|
buf_sprintf("runtime safety set twice for same scope"));
|
|
add_error_note(ira->codegen, msg, *safety_set_node_ptr, buf_sprintf("first set here"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
*safety_set_node_ptr = source_node;
|
|
*safety_off_ptr = !want_runtime_safety;
|
|
|
|
return ir_const_void(ira, &set_runtime_safety_instruction->base);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_set_float_mode(IrAnalyze *ira,
|
|
IrInstructionSetFloatMode *instruction)
|
|
{
|
|
if (ira->new_irb.exec->is_inline) {
|
|
// ignore setFloatMode when running functions at compile time
|
|
return ir_const_void(ira, &instruction->base);
|
|
}
|
|
|
|
bool *fast_math_on_ptr;
|
|
AstNode **fast_math_set_node_ptr;
|
|
|
|
Scope *scope = instruction->base.scope;
|
|
while (scope != nullptr) {
|
|
if (scope->id == ScopeIdBlock) {
|
|
ScopeBlock *block_scope = (ScopeBlock *)scope;
|
|
fast_math_on_ptr = &block_scope->fast_math_on;
|
|
fast_math_set_node_ptr = &block_scope->fast_math_set_node;
|
|
break;
|
|
} else if (scope->id == ScopeIdFnDef) {
|
|
ScopeFnDef *def_scope = (ScopeFnDef *)scope;
|
|
ZigFn *target_fn = def_scope->fn_entry;
|
|
assert(target_fn->def_scope != nullptr);
|
|
fast_math_on_ptr = &target_fn->def_scope->fast_math_on;
|
|
fast_math_set_node_ptr = &target_fn->def_scope->fast_math_set_node;
|
|
break;
|
|
} else if (scope->id == ScopeIdDecls) {
|
|
ScopeDecls *decls_scope = (ScopeDecls *)scope;
|
|
fast_math_on_ptr = &decls_scope->fast_math_on;
|
|
fast_math_set_node_ptr = &decls_scope->fast_math_set_node;
|
|
break;
|
|
} else {
|
|
scope = scope->parent;
|
|
continue;
|
|
}
|
|
}
|
|
assert(scope != nullptr);
|
|
|
|
IrInstruction *float_mode_value = instruction->mode_value->child;
|
|
FloatMode float_mode_scalar;
|
|
if (!ir_resolve_float_mode(ira, float_mode_value, &float_mode_scalar))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
AstNode *source_node = instruction->base.source_node;
|
|
if (*fast_math_set_node_ptr) {
|
|
ErrorMsg *msg = ir_add_error_node(ira, source_node,
|
|
buf_sprintf("float mode set twice for same scope"));
|
|
add_error_note(ira->codegen, msg, *fast_math_set_node_ptr, buf_sprintf("first set here"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
*fast_math_set_node_ptr = source_node;
|
|
*fast_math_on_ptr = (float_mode_scalar == FloatModeOptimized);
|
|
|
|
return ir_const_void(ira, &instruction->base);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_any_frame_type(IrAnalyze *ira,
|
|
IrInstructionAnyFrameType *instruction)
|
|
{
|
|
ZigType *payload_type = nullptr;
|
|
if (instruction->payload_type != nullptr) {
|
|
payload_type = ir_resolve_type(ira, instruction->payload_type->child);
|
|
if (type_is_invalid(payload_type))
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
ZigType *any_frame_type = get_any_frame_type(ira->codegen, payload_type);
|
|
return ir_const_type(ira, &instruction->base, any_frame_type);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira,
|
|
IrInstructionSliceType *slice_type_instruction)
|
|
{
|
|
Error err;
|
|
uint32_t align_bytes = 0;
|
|
if (slice_type_instruction->align_value != nullptr) {
|
|
if (!ir_resolve_align(ira, slice_type_instruction->align_value->child, &align_bytes))
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
ZigType *child_type = ir_resolve_type(ira, slice_type_instruction->child_type->child);
|
|
if (type_is_invalid(child_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
bool is_const = slice_type_instruction->is_const;
|
|
bool is_volatile = slice_type_instruction->is_volatile;
|
|
bool is_allow_zero = slice_type_instruction->is_allow_zero;
|
|
|
|
switch (child_type->id) {
|
|
case ZigTypeIdInvalid: // handled above
|
|
zig_unreachable();
|
|
case ZigTypeIdUnreachable:
|
|
case ZigTypeIdUndefined:
|
|
case ZigTypeIdNull:
|
|
case ZigTypeIdArgTuple:
|
|
case ZigTypeIdOpaque:
|
|
ir_add_error_node(ira, slice_type_instruction->base.source_node,
|
|
buf_sprintf("slice of type '%s' not allowed", buf_ptr(&child_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
case ZigTypeIdMetaType:
|
|
case ZigTypeIdVoid:
|
|
case ZigTypeIdBool:
|
|
case ZigTypeIdInt:
|
|
case ZigTypeIdFloat:
|
|
case ZigTypeIdPointer:
|
|
case ZigTypeIdArray:
|
|
case ZigTypeIdStruct:
|
|
case ZigTypeIdComptimeFloat:
|
|
case ZigTypeIdComptimeInt:
|
|
case ZigTypeIdEnumLiteral:
|
|
case ZigTypeIdOptional:
|
|
case ZigTypeIdErrorUnion:
|
|
case ZigTypeIdErrorSet:
|
|
case ZigTypeIdEnum:
|
|
case ZigTypeIdUnion:
|
|
case ZigTypeIdFn:
|
|
case ZigTypeIdBoundFn:
|
|
case ZigTypeIdVector:
|
|
case ZigTypeIdCoroFrame:
|
|
case ZigTypeIdAnyFrame:
|
|
{
|
|
ResolveStatus needed_status = (align_bytes == 0) ?
|
|
ResolveStatusZeroBitsKnown : ResolveStatusAlignmentKnown;
|
|
if ((err = type_resolve(ira->codegen, child_type, needed_status)))
|
|
return ira->codegen->invalid_instruction;
|
|
ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, child_type,
|
|
is_const, is_volatile, PtrLenUnknown, align_bytes, 0, 0, is_allow_zero);
|
|
ZigType *result_type = get_slice_type(ira->codegen, slice_ptr_type);
|
|
return ir_const_type(ira, &slice_type_instruction->base, result_type);
|
|
}
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_global_asm(IrAnalyze *ira, IrInstructionGlobalAsm *instruction) {
|
|
buf_append_char(&ira->codegen->global_asm, '\n');
|
|
buf_append_buf(&ira->codegen->global_asm, instruction->asm_code);
|
|
|
|
return ir_const_void(ira, &instruction->base);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstructionAsm *asm_instruction) {
|
|
assert(asm_instruction->base.source_node->type == NodeTypeAsmExpr);
|
|
|
|
AstNodeAsmExpr *asm_expr = &asm_instruction->base.source_node->data.asm_expr;
|
|
|
|
if (!ir_emit_global_runtime_side_effect(ira, &asm_instruction->base))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
// TODO validate the output types and variable types
|
|
|
|
IrInstruction **input_list = allocate<IrInstruction *>(asm_expr->input_list.length);
|
|
IrInstruction **output_types = allocate<IrInstruction *>(asm_expr->output_list.length);
|
|
|
|
ZigType *return_type = ira->codegen->builtin_types.entry_void;
|
|
for (size_t i = 0; i < asm_expr->output_list.length; i += 1) {
|
|
AsmOutput *asm_output = asm_expr->output_list.at(i);
|
|
if (asm_output->return_type) {
|
|
output_types[i] = asm_instruction->output_types[i]->child;
|
|
return_type = ir_resolve_type(ira, output_types[i]);
|
|
if (type_is_invalid(return_type))
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
|
|
for (size_t i = 0; i < asm_expr->input_list.length; i += 1) {
|
|
IrInstruction *const input_value = asm_instruction->input_list[i]->child;
|
|
if (type_is_invalid(input_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (instr_is_comptime(input_value) &&
|
|
(input_value->value.type->id == ZigTypeIdComptimeInt ||
|
|
input_value->value.type->id == ZigTypeIdComptimeFloat)) {
|
|
ir_add_error_node(ira, input_value->source_node,
|
|
buf_sprintf("expected sized integer or sized float, found %s", buf_ptr(&input_value->value.type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
input_list[i] = input_value;
|
|
}
|
|
|
|
IrInstruction *result = ir_build_asm(&ira->new_irb,
|
|
asm_instruction->base.scope, asm_instruction->base.source_node,
|
|
asm_instruction->asm_template, asm_instruction->token_list, asm_instruction->token_list_len,
|
|
input_list, output_types, asm_instruction->output_vars, asm_instruction->return_count,
|
|
asm_instruction->has_side_effects);
|
|
result->value.type = return_type;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_array_type(IrAnalyze *ira,
|
|
IrInstructionArrayType *array_type_instruction)
|
|
{
|
|
Error err;
|
|
|
|
IrInstruction *size_value = array_type_instruction->size->child;
|
|
uint64_t size;
|
|
if (!ir_resolve_usize(ira, size_value, &size))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *child_type_value = array_type_instruction->child_type->child;
|
|
ZigType *child_type = ir_resolve_type(ira, child_type_value);
|
|
if (type_is_invalid(child_type))
|
|
return ira->codegen->invalid_instruction;
|
|
switch (child_type->id) {
|
|
case ZigTypeIdInvalid: // handled above
|
|
zig_unreachable();
|
|
case ZigTypeIdUnreachable:
|
|
case ZigTypeIdUndefined:
|
|
case ZigTypeIdNull:
|
|
case ZigTypeIdArgTuple:
|
|
case ZigTypeIdOpaque:
|
|
ir_add_error_node(ira, array_type_instruction->base.source_node,
|
|
buf_sprintf("array of type '%s' not allowed", buf_ptr(&child_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
case ZigTypeIdMetaType:
|
|
case ZigTypeIdVoid:
|
|
case ZigTypeIdBool:
|
|
case ZigTypeIdInt:
|
|
case ZigTypeIdFloat:
|
|
case ZigTypeIdPointer:
|
|
case ZigTypeIdArray:
|
|
case ZigTypeIdStruct:
|
|
case ZigTypeIdComptimeFloat:
|
|
case ZigTypeIdComptimeInt:
|
|
case ZigTypeIdEnumLiteral:
|
|
case ZigTypeIdOptional:
|
|
case ZigTypeIdErrorUnion:
|
|
case ZigTypeIdErrorSet:
|
|
case ZigTypeIdEnum:
|
|
case ZigTypeIdUnion:
|
|
case ZigTypeIdFn:
|
|
case ZigTypeIdBoundFn:
|
|
case ZigTypeIdVector:
|
|
case ZigTypeIdCoroFrame:
|
|
case ZigTypeIdAnyFrame:
|
|
{
|
|
if ((err = ensure_complete_type(ira->codegen, child_type)))
|
|
return ira->codegen->invalid_instruction;
|
|
ZigType *result_type = get_array_type(ira->codegen, child_type, size);
|
|
return ir_const_type(ira, &array_type_instruction->base, result_type);
|
|
}
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_size_of(IrAnalyze *ira,
|
|
IrInstructionSizeOf *size_of_instruction)
|
|
{
|
|
Error err;
|
|
IrInstruction *type_value = size_of_instruction->type_value->child;
|
|
ZigType *type_entry = ir_resolve_type(ira, type_value);
|
|
|
|
if ((err = ensure_complete_type(ira->codegen, type_entry)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
switch (type_entry->id) {
|
|
case ZigTypeIdInvalid: // handled above
|
|
zig_unreachable();
|
|
case ZigTypeIdUnreachable:
|
|
case ZigTypeIdUndefined:
|
|
case ZigTypeIdNull:
|
|
case ZigTypeIdBoundFn:
|
|
case ZigTypeIdArgTuple:
|
|
case ZigTypeIdOpaque:
|
|
ir_add_error_node(ira, type_value->source_node,
|
|
buf_sprintf("no size available for type '%s'", buf_ptr(&type_entry->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
case ZigTypeIdMetaType:
|
|
case ZigTypeIdEnumLiteral:
|
|
case ZigTypeIdComptimeFloat:
|
|
case ZigTypeIdComptimeInt:
|
|
case ZigTypeIdVoid:
|
|
case ZigTypeIdBool:
|
|
case ZigTypeIdInt:
|
|
case ZigTypeIdFloat:
|
|
case ZigTypeIdPointer:
|
|
case ZigTypeIdArray:
|
|
case ZigTypeIdStruct:
|
|
case ZigTypeIdOptional:
|
|
case ZigTypeIdErrorUnion:
|
|
case ZigTypeIdErrorSet:
|
|
case ZigTypeIdEnum:
|
|
case ZigTypeIdUnion:
|
|
case ZigTypeIdFn:
|
|
case ZigTypeIdVector:
|
|
case ZigTypeIdCoroFrame:
|
|
case ZigTypeIdAnyFrame:
|
|
{
|
|
uint64_t size_in_bytes = type_size(ira->codegen, type_entry);
|
|
return ir_const_unsigned(ira, &size_of_instruction->base, size_in_bytes);
|
|
}
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_test_non_null(IrAnalyze *ira, IrInstruction *source_inst, IrInstruction *value) {
|
|
ZigType *type_entry = value->value.type;
|
|
|
|
if (type_entry->id == ZigTypeIdPointer && type_entry->data.pointer.allow_zero) {
|
|
if (instr_is_comptime(value)) {
|
|
ConstExprValue *c_ptr_val = ir_resolve_const(ira, value, UndefOk);
|
|
if (c_ptr_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
if (c_ptr_val->special == ConstValSpecialUndef)
|
|
return ir_const_undef(ira, source_inst, ira->codegen->builtin_types.entry_bool);
|
|
bool is_null = c_ptr_val->data.x_ptr.special == ConstPtrSpecialNull ||
|
|
(c_ptr_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr &&
|
|
c_ptr_val->data.x_ptr.data.hard_coded_addr.addr == 0);
|
|
return ir_const_bool(ira, source_inst, !is_null);
|
|
}
|
|
|
|
IrInstruction *result = ir_build_test_nonnull(&ira->new_irb,
|
|
source_inst->scope, source_inst->source_node, value);
|
|
result->value.type = ira->codegen->builtin_types.entry_bool;
|
|
return result;
|
|
} else if (type_entry->id == ZigTypeIdOptional) {
|
|
if (instr_is_comptime(value)) {
|
|
ConstExprValue *maybe_val = ir_resolve_const(ira, value, UndefOk);
|
|
if (maybe_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
if (maybe_val->special == ConstValSpecialUndef)
|
|
return ir_const_undef(ira, source_inst, ira->codegen->builtin_types.entry_bool);
|
|
|
|
return ir_const_bool(ira, source_inst, !optional_value_is_null(maybe_val));
|
|
}
|
|
|
|
IrInstruction *result = ir_build_test_nonnull(&ira->new_irb,
|
|
source_inst->scope, source_inst->source_node, value);
|
|
result->value.type = ira->codegen->builtin_types.entry_bool;
|
|
return result;
|
|
} else if (type_entry->id == ZigTypeIdNull) {
|
|
return ir_const_bool(ira, source_inst, false);
|
|
} else {
|
|
return ir_const_bool(ira, source_inst, true);
|
|
}
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrInstructionTestNonNull *instruction) {
|
|
IrInstruction *value = instruction->value->child;
|
|
if (type_is_invalid(value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
return ir_analyze_test_non_null(ira, &instruction->base, value);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInstruction *source_instr,
|
|
IrInstruction *base_ptr, bool safety_check_on, bool initializing)
|
|
{
|
|
ZigType *ptr_type = base_ptr->value.type;
|
|
assert(ptr_type->id == ZigTypeIdPointer);
|
|
|
|
ZigType *type_entry = ptr_type->data.pointer.child_type;
|
|
if (type_is_invalid(type_entry))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (type_entry->id == ZigTypeIdPointer && type_entry->data.pointer.ptr_len == PtrLenC) {
|
|
if (instr_is_comptime(base_ptr)) {
|
|
ConstExprValue *val = ir_resolve_const(ira, base_ptr, UndefBad);
|
|
if (!val)
|
|
return ira->codegen->invalid_instruction;
|
|
if (val->data.x_ptr.mut != ConstPtrMutRuntimeVar) {
|
|
ConstExprValue *c_ptr_val = const_ptr_pointee(ira, ira->codegen, val, source_instr->source_node);
|
|
if (c_ptr_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
bool is_null = c_ptr_val->data.x_ptr.special == ConstPtrSpecialNull ||
|
|
(c_ptr_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr &&
|
|
c_ptr_val->data.x_ptr.data.hard_coded_addr.addr == 0);
|
|
if (is_null) {
|
|
ir_add_error(ira, source_instr, buf_sprintf("unable to unwrap null"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
return base_ptr;
|
|
}
|
|
}
|
|
if (!safety_check_on)
|
|
return base_ptr;
|
|
IrInstruction *c_ptr_val = ir_get_deref(ira, source_instr, base_ptr, nullptr);
|
|
ir_build_assert_non_null(ira, source_instr, c_ptr_val);
|
|
return base_ptr;
|
|
}
|
|
|
|
if (type_entry->id != ZigTypeIdOptional) {
|
|
ir_add_error_node(ira, base_ptr->source_node,
|
|
buf_sprintf("expected optional type, found '%s'", buf_ptr(&type_entry->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
ZigType *child_type = type_entry->data.maybe.child_type;
|
|
ZigType *result_type = get_pointer_to_type_extra(ira->codegen, child_type,
|
|
ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, PtrLenSingle, 0, 0, 0, false);
|
|
|
|
bool same_comptime_repr = types_have_same_zig_comptime_repr(type_entry, child_type);
|
|
|
|
if (instr_is_comptime(base_ptr)) {
|
|
ConstExprValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad);
|
|
if (!ptr_val)
|
|
return ira->codegen->invalid_instruction;
|
|
if (ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar) {
|
|
ConstExprValue *optional_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node);
|
|
if (optional_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (initializing) {
|
|
switch (type_has_one_possible_value(ira->codegen, child_type)) {
|
|
case OnePossibleValueInvalid:
|
|
return ira->codegen->invalid_instruction;
|
|
case OnePossibleValueNo:
|
|
if (!same_comptime_repr) {
|
|
ConstExprValue *payload_val = create_const_vals(1);
|
|
payload_val->type = child_type;
|
|
payload_val->special = ConstValSpecialUndef;
|
|
payload_val->parent.id = ConstParentIdOptionalPayload;
|
|
payload_val->parent.data.p_optional_payload.optional_val = optional_val;
|
|
|
|
optional_val->data.x_optional = payload_val;
|
|
optional_val->special = ConstValSpecialStatic;
|
|
}
|
|
break;
|
|
case OnePossibleValueYes: {
|
|
ConstExprValue *pointee = create_const_vals(1);
|
|
pointee->special = ConstValSpecialStatic;
|
|
pointee->type = child_type;
|
|
pointee->parent.id = ConstParentIdOptionalPayload;
|
|
pointee->parent.data.p_optional_payload.optional_val = optional_val;
|
|
|
|
optional_val->special = ConstValSpecialStatic;
|
|
optional_val->data.x_optional = pointee;
|
|
break;
|
|
}
|
|
}
|
|
} else if (optional_value_is_null(optional_val)) {
|
|
ir_add_error(ira, source_instr, buf_sprintf("unable to unwrap null"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *result;
|
|
if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) {
|
|
result = ir_build_optional_unwrap_ptr(&ira->new_irb, source_instr->scope,
|
|
source_instr->source_node, base_ptr, false, initializing);
|
|
result->value.type = result_type;
|
|
result->value.special = ConstValSpecialStatic;
|
|
} else {
|
|
result = ir_const(ira, source_instr, result_type);
|
|
}
|
|
ConstExprValue *result_val = &result->value;
|
|
result_val->data.x_ptr.special = ConstPtrSpecialRef;
|
|
result_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut;
|
|
switch (type_has_one_possible_value(ira->codegen, child_type)) {
|
|
case OnePossibleValueInvalid:
|
|
return ira->codegen->invalid_instruction;
|
|
case OnePossibleValueNo:
|
|
if (same_comptime_repr) {
|
|
result_val->data.x_ptr.data.ref.pointee = optional_val;
|
|
} else {
|
|
assert(optional_val->data.x_optional != nullptr);
|
|
result_val->data.x_ptr.data.ref.pointee = optional_val->data.x_optional;
|
|
}
|
|
break;
|
|
case OnePossibleValueYes:
|
|
assert(optional_val->data.x_optional != nullptr);
|
|
result_val->data.x_ptr.data.ref.pointee = optional_val->data.x_optional;
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
IrInstruction *result = ir_build_optional_unwrap_ptr(&ira->new_irb, source_instr->scope,
|
|
source_instr->source_node, base_ptr, safety_check_on, initializing);
|
|
result->value.type = result_type;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_optional_unwrap_ptr(IrAnalyze *ira,
|
|
IrInstructionOptionalUnwrapPtr *instruction)
|
|
{
|
|
IrInstruction *base_ptr = instruction->base_ptr->child;
|
|
if (type_is_invalid(base_ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
return ir_analyze_unwrap_optional_payload(ira, &instruction->base, base_ptr,
|
|
instruction->safety_check_on, false);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_ctz(IrAnalyze *ira, IrInstructionCtz *instruction) {
|
|
ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child);
|
|
if (type_is_invalid(int_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *op = ir_implicit_cast(ira, instruction->op->child, int_type);
|
|
if (type_is_invalid(op->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (int_type->data.integral.bit_count == 0)
|
|
return ir_const_unsigned(ira, &instruction->base, 0);
|
|
|
|
if (instr_is_comptime(op)) {
|
|
ConstExprValue *val = ir_resolve_const(ira, op, UndefOk);
|
|
if (val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
if (val->special == ConstValSpecialUndef)
|
|
return ir_const_undef(ira, &instruction->base, ira->codegen->builtin_types.entry_num_lit_int);
|
|
size_t result_usize = bigint_ctz(&op->value.data.x_bigint, int_type->data.integral.bit_count);
|
|
return ir_const_unsigned(ira, &instruction->base, result_usize);
|
|
}
|
|
|
|
ZigType *return_type = get_smallest_unsigned_int_type(ira->codegen, int_type->data.integral.bit_count);
|
|
IrInstruction *result = ir_build_ctz(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node, nullptr, op);
|
|
result->value.type = return_type;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_clz(IrAnalyze *ira, IrInstructionClz *instruction) {
|
|
ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child);
|
|
if (type_is_invalid(int_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *op = ir_implicit_cast(ira, instruction->op->child, int_type);
|
|
if (type_is_invalid(op->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (int_type->data.integral.bit_count == 0)
|
|
return ir_const_unsigned(ira, &instruction->base, 0);
|
|
|
|
if (instr_is_comptime(op)) {
|
|
ConstExprValue *val = ir_resolve_const(ira, op, UndefOk);
|
|
if (val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
if (val->special == ConstValSpecialUndef)
|
|
return ir_const_undef(ira, &instruction->base, ira->codegen->builtin_types.entry_num_lit_int);
|
|
size_t result_usize = bigint_clz(&op->value.data.x_bigint, int_type->data.integral.bit_count);
|
|
return ir_const_unsigned(ira, &instruction->base, result_usize);
|
|
}
|
|
|
|
ZigType *return_type = get_smallest_unsigned_int_type(ira->codegen, int_type->data.integral.bit_count);
|
|
IrInstruction *result = ir_build_clz(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node, nullptr, op);
|
|
result->value.type = return_type;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_pop_count(IrAnalyze *ira, IrInstructionPopCount *instruction) {
|
|
ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child);
|
|
if (type_is_invalid(int_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *op = ir_implicit_cast(ira, instruction->op->child, int_type);
|
|
if (type_is_invalid(op->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (int_type->data.integral.bit_count == 0)
|
|
return ir_const_unsigned(ira, &instruction->base, 0);
|
|
|
|
if (instr_is_comptime(op)) {
|
|
ConstExprValue *val = ir_resolve_const(ira, op, UndefOk);
|
|
if (val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
if (val->special == ConstValSpecialUndef)
|
|
return ir_const_undef(ira, &instruction->base, ira->codegen->builtin_types.entry_num_lit_int);
|
|
|
|
if (bigint_cmp_zero(&val->data.x_bigint) != CmpLT) {
|
|
size_t result = bigint_popcount_unsigned(&val->data.x_bigint);
|
|
return ir_const_unsigned(ira, &instruction->base, result);
|
|
}
|
|
size_t result = bigint_popcount_signed(&val->data.x_bigint, int_type->data.integral.bit_count);
|
|
return ir_const_unsigned(ira, &instruction->base, result);
|
|
}
|
|
|
|
ZigType *return_type = get_smallest_unsigned_int_type(ira->codegen, int_type->data.integral.bit_count);
|
|
IrInstruction *result = ir_build_pop_count(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node, nullptr, op);
|
|
result->value.type = return_type;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_union_tag(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value) {
|
|
if (type_is_invalid(value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (value->value.type->id == ZigTypeIdEnum) {
|
|
return value;
|
|
}
|
|
|
|
if (value->value.type->id != ZigTypeIdUnion) {
|
|
ir_add_error(ira, value,
|
|
buf_sprintf("expected enum or union type, found '%s'", buf_ptr(&value->value.type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
if (!value->value.type->data.unionation.have_explicit_tag_type && !source_instr->is_gen) {
|
|
ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("union has no associated enum"));
|
|
if (value->value.type->data.unionation.decl_node != nullptr) {
|
|
add_error_note(ira->codegen, msg, value->value.type->data.unionation.decl_node,
|
|
buf_sprintf("declared here"));
|
|
}
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
ZigType *tag_type = value->value.type->data.unionation.tag_type;
|
|
assert(tag_type->id == ZigTypeIdEnum);
|
|
|
|
if (instr_is_comptime(value)) {
|
|
ConstExprValue *val = ir_resolve_const(ira, value, UndefBad);
|
|
if (!val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb,
|
|
source_instr->scope, source_instr->source_node);
|
|
const_instruction->base.value.type = tag_type;
|
|
const_instruction->base.value.special = ConstValSpecialStatic;
|
|
bigint_init_bigint(&const_instruction->base.value.data.x_enum_tag, &val->data.x_union.tag);
|
|
return &const_instruction->base;
|
|
}
|
|
|
|
IrInstruction *result = ir_build_union_tag(&ira->new_irb, source_instr->scope, source_instr->source_node, value);
|
|
result->value.type = tag_type;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_switch_br(IrAnalyze *ira,
|
|
IrInstructionSwitchBr *switch_br_instruction)
|
|
{
|
|
IrInstruction *target_value = switch_br_instruction->target_value->child;
|
|
if (type_is_invalid(target_value->value.type))
|
|
return ir_unreach_error(ira);
|
|
|
|
if (switch_br_instruction->switch_prongs_void != nullptr) {
|
|
if (type_is_invalid(switch_br_instruction->switch_prongs_void->child->value.type)) {
|
|
return ir_unreach_error(ira);
|
|
}
|
|
}
|
|
|
|
|
|
size_t case_count = switch_br_instruction->case_count;
|
|
|
|
bool is_comptime;
|
|
if (!ir_resolve_comptime(ira, switch_br_instruction->is_comptime->child, &is_comptime))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (is_comptime || instr_is_comptime(target_value)) {
|
|
ConstExprValue *target_val = ir_resolve_const(ira, target_value, UndefBad);
|
|
if (!target_val)
|
|
return ir_unreach_error(ira);
|
|
|
|
IrBasicBlock *old_dest_block = switch_br_instruction->else_block;
|
|
for (size_t i = 0; i < case_count; i += 1) {
|
|
IrInstructionSwitchBrCase *old_case = &switch_br_instruction->cases[i];
|
|
IrInstruction *case_value = old_case->value->child;
|
|
if (type_is_invalid(case_value->value.type))
|
|
return ir_unreach_error(ira);
|
|
|
|
if (case_value->value.type->id == ZigTypeIdEnum) {
|
|
case_value = ir_analyze_union_tag(ira, &switch_br_instruction->base, case_value);
|
|
if (type_is_invalid(case_value->value.type))
|
|
return ir_unreach_error(ira);
|
|
}
|
|
|
|
IrInstruction *casted_case_value = ir_implicit_cast(ira, case_value, target_value->value.type);
|
|
if (type_is_invalid(casted_case_value->value.type))
|
|
return ir_unreach_error(ira);
|
|
|
|
ConstExprValue *case_val = ir_resolve_const(ira, casted_case_value, UndefBad);
|
|
if (!case_val)
|
|
return ir_unreach_error(ira);
|
|
|
|
if (const_values_equal(ira->codegen, target_val, case_val)) {
|
|
old_dest_block = old_case->block;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (is_comptime || old_dest_block->ref_count == 1) {
|
|
return ir_inline_bb(ira, &switch_br_instruction->base, old_dest_block);
|
|
} else {
|
|
IrBasicBlock *new_dest_block = ir_get_new_bb(ira, old_dest_block, &switch_br_instruction->base);
|
|
IrInstruction *result = ir_build_br(&ira->new_irb,
|
|
switch_br_instruction->base.scope, switch_br_instruction->base.source_node,
|
|
new_dest_block, nullptr);
|
|
result->value.type = ira->codegen->builtin_types.entry_unreachable;
|
|
return ir_finish_anal(ira, result);
|
|
}
|
|
}
|
|
|
|
IrInstructionSwitchBrCase *cases = allocate<IrInstructionSwitchBrCase>(case_count);
|
|
for (size_t i = 0; i < case_count; i += 1) {
|
|
IrInstructionSwitchBrCase *old_case = &switch_br_instruction->cases[i];
|
|
IrInstructionSwitchBrCase *new_case = &cases[i];
|
|
new_case->block = ir_get_new_bb(ira, old_case->block, &switch_br_instruction->base);
|
|
new_case->value = ira->codegen->invalid_instruction;
|
|
|
|
// Calling ir_get_new_bb set the ref_instruction on the new basic block.
|
|
// However a switch br may branch to the same basic block which would trigger an
|
|
// incorrect re-generation of the block. So we set it to null here and assign
|
|
// it back after the loop.
|
|
new_case->block->ref_instruction = nullptr;
|
|
|
|
IrInstruction *old_value = old_case->value;
|
|
IrInstruction *new_value = old_value->child;
|
|
if (type_is_invalid(new_value->value.type))
|
|
continue;
|
|
|
|
if (new_value->value.type->id == ZigTypeIdEnum) {
|
|
new_value = ir_analyze_union_tag(ira, &switch_br_instruction->base, new_value);
|
|
if (type_is_invalid(new_value->value.type))
|
|
continue;
|
|
}
|
|
|
|
IrInstruction *casted_new_value = ir_implicit_cast(ira, new_value, target_value->value.type);
|
|
if (type_is_invalid(casted_new_value->value.type))
|
|
continue;
|
|
|
|
if (!ir_resolve_const(ira, casted_new_value, UndefBad))
|
|
continue;
|
|
|
|
new_case->value = casted_new_value;
|
|
}
|
|
|
|
for (size_t i = 0; i < case_count; i += 1) {
|
|
IrInstructionSwitchBrCase *new_case = &cases[i];
|
|
if (new_case->value == ira->codegen->invalid_instruction)
|
|
return ir_unreach_error(ira);
|
|
new_case->block->ref_instruction = &switch_br_instruction->base;
|
|
}
|
|
|
|
IrBasicBlock *new_else_block = ir_get_new_bb(ira, switch_br_instruction->else_block, &switch_br_instruction->base);
|
|
IrInstructionSwitchBr *switch_br = ir_build_switch_br(&ira->new_irb,
|
|
switch_br_instruction->base.scope, switch_br_instruction->base.source_node,
|
|
target_value, new_else_block, case_count, cases, nullptr, nullptr);
|
|
switch_br->base.value.type = ira->codegen->builtin_types.entry_unreachable;
|
|
return ir_finish_anal(ira, &switch_br->base);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira,
|
|
IrInstructionSwitchTarget *switch_target_instruction)
|
|
{
|
|
Error err;
|
|
IrInstruction *target_value_ptr = switch_target_instruction->target_value_ptr->child;
|
|
if (type_is_invalid(target_value_ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (target_value_ptr->value.type->id == ZigTypeIdMetaType) {
|
|
assert(instr_is_comptime(target_value_ptr));
|
|
ZigType *ptr_type = target_value_ptr->value.data.x_type;
|
|
assert(ptr_type->id == ZigTypeIdPointer);
|
|
return ir_const_type(ira, &switch_target_instruction->base, ptr_type->data.pointer.child_type);
|
|
}
|
|
|
|
ZigType *target_type = target_value_ptr->value.type->data.pointer.child_type;
|
|
ConstExprValue *pointee_val = nullptr;
|
|
if (instr_is_comptime(target_value_ptr)) {
|
|
pointee_val = const_ptr_pointee(ira, ira->codegen, &target_value_ptr->value, target_value_ptr->source_node);
|
|
if (pointee_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (pointee_val->special == ConstValSpecialRuntime)
|
|
pointee_val = nullptr;
|
|
}
|
|
if ((err = ensure_complete_type(ira->codegen, target_type)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
switch (target_type->id) {
|
|
case ZigTypeIdInvalid:
|
|
zig_unreachable();
|
|
case ZigTypeIdMetaType:
|
|
case ZigTypeIdVoid:
|
|
case ZigTypeIdBool:
|
|
case ZigTypeIdInt:
|
|
case ZigTypeIdFloat:
|
|
case ZigTypeIdComptimeFloat:
|
|
case ZigTypeIdComptimeInt:
|
|
case ZigTypeIdEnumLiteral:
|
|
case ZigTypeIdPointer:
|
|
case ZigTypeIdFn:
|
|
case ZigTypeIdErrorSet: {
|
|
if (pointee_val) {
|
|
IrInstruction *result = ir_const(ira, &switch_target_instruction->base, nullptr);
|
|
copy_const_val(&result->value, pointee_val, true);
|
|
result->value.type = target_type;
|
|
return result;
|
|
}
|
|
|
|
IrInstruction *result = ir_get_deref(ira, &switch_target_instruction->base, target_value_ptr, nullptr);
|
|
result->value.type = target_type;
|
|
return result;
|
|
}
|
|
case ZigTypeIdUnion: {
|
|
AstNode *decl_node = target_type->data.unionation.decl_node;
|
|
if (!decl_node->data.container_decl.auto_enum &&
|
|
decl_node->data.container_decl.init_arg_expr == nullptr)
|
|
{
|
|
ErrorMsg *msg = ir_add_error(ira, target_value_ptr,
|
|
buf_sprintf("switch on union which has no attached enum"));
|
|
add_error_note(ira->codegen, msg, decl_node,
|
|
buf_sprintf("consider 'union(enum)' here"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
ZigType *tag_type = target_type->data.unionation.tag_type;
|
|
assert(tag_type != nullptr);
|
|
assert(tag_type->id == ZigTypeIdEnum);
|
|
if (pointee_val) {
|
|
IrInstruction *result = ir_const(ira, &switch_target_instruction->base, tag_type);
|
|
bigint_init_bigint(&result->value.data.x_enum_tag, &pointee_val->data.x_union.tag);
|
|
return result;
|
|
}
|
|
if (tag_type->data.enumeration.src_field_count == 1) {
|
|
IrInstruction *result = ir_const(ira, &switch_target_instruction->base, tag_type);
|
|
TypeEnumField *only_field = &tag_type->data.enumeration.fields[0];
|
|
bigint_init_bigint(&result->value.data.x_enum_tag, &only_field->value);
|
|
return result;
|
|
}
|
|
|
|
IrInstruction *union_value = ir_get_deref(ira, &switch_target_instruction->base, target_value_ptr, nullptr);
|
|
union_value->value.type = target_type;
|
|
|
|
IrInstruction *union_tag_inst = ir_build_union_tag(&ira->new_irb, switch_target_instruction->base.scope,
|
|
switch_target_instruction->base.source_node, union_value);
|
|
union_tag_inst->value.type = tag_type;
|
|
return union_tag_inst;
|
|
}
|
|
case ZigTypeIdEnum: {
|
|
if ((err = type_resolve(ira->codegen, target_type, ResolveStatusZeroBitsKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
if (target_type->data.enumeration.src_field_count < 2) {
|
|
TypeEnumField *only_field = &target_type->data.enumeration.fields[0];
|
|
IrInstruction *result = ir_const(ira, &switch_target_instruction->base, target_type);
|
|
bigint_init_bigint(&result->value.data.x_enum_tag, &only_field->value);
|
|
return result;
|
|
}
|
|
|
|
if (pointee_val) {
|
|
IrInstruction *result = ir_const(ira, &switch_target_instruction->base, target_type);
|
|
bigint_init_bigint(&result->value.data.x_enum_tag, &pointee_val->data.x_enum_tag);
|
|
return result;
|
|
}
|
|
|
|
IrInstruction *enum_value = ir_get_deref(ira, &switch_target_instruction->base, target_value_ptr, nullptr);
|
|
enum_value->value.type = target_type;
|
|
return enum_value;
|
|
}
|
|
case ZigTypeIdErrorUnion:
|
|
case ZigTypeIdUnreachable:
|
|
case ZigTypeIdArray:
|
|
case ZigTypeIdStruct:
|
|
case ZigTypeIdUndefined:
|
|
case ZigTypeIdNull:
|
|
case ZigTypeIdOptional:
|
|
case ZigTypeIdBoundFn:
|
|
case ZigTypeIdArgTuple:
|
|
case ZigTypeIdOpaque:
|
|
case ZigTypeIdVector:
|
|
case ZigTypeIdCoroFrame:
|
|
case ZigTypeIdAnyFrame:
|
|
ir_add_error(ira, &switch_target_instruction->base,
|
|
buf_sprintf("invalid switch target type '%s'", buf_ptr(&target_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstructionSwitchVar *instruction) {
|
|
IrInstruction *target_value_ptr = instruction->target_value_ptr->child;
|
|
if (type_is_invalid(target_value_ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *ref_type = target_value_ptr->value.type;
|
|
assert(ref_type->id == ZigTypeIdPointer);
|
|
ZigType *target_type = target_value_ptr->value.type->data.pointer.child_type;
|
|
if (target_type->id == ZigTypeIdUnion) {
|
|
ZigType *enum_type = target_type->data.unionation.tag_type;
|
|
assert(enum_type != nullptr);
|
|
assert(enum_type->id == ZigTypeIdEnum);
|
|
assert(instruction->prongs_len > 0);
|
|
|
|
IrInstruction *first_prong_value = instruction->prongs_ptr[0]->child;
|
|
if (type_is_invalid(first_prong_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *first_casted_prong_value = ir_implicit_cast(ira, first_prong_value, enum_type);
|
|
if (type_is_invalid(first_casted_prong_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ConstExprValue *first_prong_val = ir_resolve_const(ira, first_casted_prong_value, UndefBad);
|
|
if (first_prong_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
TypeUnionField *first_field = find_union_field_by_tag(target_type, &first_prong_val->data.x_enum_tag);
|
|
|
|
ErrorMsg *invalid_payload_msg = nullptr;
|
|
for (size_t prong_i = 1; prong_i < instruction->prongs_len; prong_i += 1) {
|
|
IrInstruction *this_prong_inst = instruction->prongs_ptr[prong_i]->child;
|
|
if (type_is_invalid(this_prong_inst->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *this_casted_prong_value = ir_implicit_cast(ira, this_prong_inst, enum_type);
|
|
if (type_is_invalid(this_casted_prong_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ConstExprValue *this_prong = ir_resolve_const(ira, this_casted_prong_value, UndefBad);
|
|
if (this_prong == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
TypeUnionField *payload_field = find_union_field_by_tag(target_type, &this_prong->data.x_enum_tag);
|
|
ZigType *payload_type = payload_field->type_entry;
|
|
if (first_field->type_entry != payload_type) {
|
|
if (invalid_payload_msg == nullptr) {
|
|
invalid_payload_msg = ir_add_error(ira, &instruction->base,
|
|
buf_sprintf("capture group with incompatible types"));
|
|
add_error_note(ira->codegen, invalid_payload_msg, first_prong_value->source_node,
|
|
buf_sprintf("type '%s' here", buf_ptr(&first_field->type_entry->name)));
|
|
}
|
|
add_error_note(ira->codegen, invalid_payload_msg, this_prong_inst->source_node,
|
|
buf_sprintf("type '%s' here", buf_ptr(&payload_field->type_entry->name)));
|
|
}
|
|
}
|
|
|
|
if (invalid_payload_msg != nullptr) {
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (instr_is_comptime(target_value_ptr)) {
|
|
ConstExprValue *target_val_ptr = ir_resolve_const(ira, target_value_ptr, UndefBad);
|
|
if (!target_value_ptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ConstExprValue *pointee_val = const_ptr_pointee(ira, ira->codegen, target_val_ptr, instruction->base.source_node);
|
|
if (pointee_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *result = ir_const(ira, &instruction->base,
|
|
get_pointer_to_type(ira->codegen, first_field->type_entry,
|
|
target_val_ptr->type->data.pointer.is_const));
|
|
ConstExprValue *out_val = &result->value;
|
|
out_val->data.x_ptr.special = ConstPtrSpecialRef;
|
|
out_val->data.x_ptr.mut = target_val_ptr->data.x_ptr.mut;
|
|
out_val->data.x_ptr.data.ref.pointee = pointee_val->data.x_union.payload;
|
|
return result;
|
|
}
|
|
|
|
IrInstruction *result = ir_build_union_field_ptr(&ira->new_irb,
|
|
instruction->base.scope, instruction->base.source_node, target_value_ptr, first_field, false, false);
|
|
result->value.type = get_pointer_to_type(ira->codegen, first_field->type_entry,
|
|
target_value_ptr->value.type->data.pointer.is_const);
|
|
return result;
|
|
} else if (target_type->id == ZigTypeIdErrorSet) {
|
|
// construct an error set from the prong values
|
|
ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet);
|
|
err_set_type->size_in_bits = ira->codegen->builtin_types.entry_global_error_set->size_in_bits;
|
|
err_set_type->abi_align = ira->codegen->builtin_types.entry_global_error_set->abi_align;
|
|
err_set_type->abi_size = ira->codegen->builtin_types.entry_global_error_set->abi_size;
|
|
ZigList<ErrorTableEntry *> error_list = {};
|
|
buf_resize(&err_set_type->name, 0);
|
|
buf_appendf(&err_set_type->name, "error{");
|
|
for (size_t i = 0; i < instruction->prongs_len; i += 1) {
|
|
ErrorTableEntry *err = ir_resolve_error(ira, instruction->prongs_ptr[i]->child);
|
|
if (err == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
error_list.append(err);
|
|
buf_appendf(&err_set_type->name, "%s,", buf_ptr(&err->name));
|
|
}
|
|
err_set_type->data.error_set.errors = error_list.items;
|
|
err_set_type->data.error_set.err_count = error_list.length;
|
|
buf_appendf(&err_set_type->name, "}");
|
|
|
|
|
|
ZigType *new_target_value_ptr_type = get_pointer_to_type_extra(ira->codegen,
|
|
err_set_type,
|
|
ref_type->data.pointer.is_const, ref_type->data.pointer.is_volatile,
|
|
ref_type->data.pointer.ptr_len,
|
|
ref_type->data.pointer.explicit_alignment,
|
|
ref_type->data.pointer.bit_offset_in_host, ref_type->data.pointer.host_int_bytes,
|
|
ref_type->data.pointer.allow_zero);
|
|
return ir_analyze_ptr_cast(ira, &instruction->base, target_value_ptr, new_target_value_ptr_type,
|
|
&instruction->base, false);
|
|
} else {
|
|
ir_add_error(ira, &instruction->base,
|
|
buf_sprintf("switch on type '%s' provides no expression parameter", buf_ptr(&target_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_switch_else_var(IrAnalyze *ira,
|
|
IrInstructionSwitchElseVar *instruction)
|
|
{
|
|
IrInstruction *target_value_ptr = instruction->target_value_ptr->child;
|
|
if (type_is_invalid(target_value_ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *ref_type = target_value_ptr->value.type;
|
|
assert(ref_type->id == ZigTypeIdPointer);
|
|
ZigType *target_type = target_value_ptr->value.type->data.pointer.child_type;
|
|
if (target_type->id == ZigTypeIdErrorSet) {
|
|
// make a new set that has the other cases removed
|
|
if (!resolve_inferred_error_set(ira->codegen, target_type, instruction->base.source_node)) {
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
if (type_is_global_error_set(target_type)) {
|
|
// the type of the else capture variable still has to be the global error set.
|
|
// once the runtime hint system is more sophisticated, we could add some hint information here.
|
|
return target_value_ptr;
|
|
}
|
|
// Make note of the errors handled by other cases
|
|
ErrorTableEntry **errors = allocate<ErrorTableEntry *>(ira->codegen->errors_by_index.length);
|
|
for (size_t case_i = 0; case_i < instruction->switch_br->case_count; case_i += 1) {
|
|
IrInstructionSwitchBrCase *br_case = &instruction->switch_br->cases[case_i];
|
|
IrInstruction *case_expr = br_case->value->child;
|
|
if (case_expr->value.type->id == ZigTypeIdErrorSet) {
|
|
ErrorTableEntry *err = ir_resolve_error(ira, case_expr);
|
|
if (err == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
errors[err->value] = err;
|
|
} else if (case_expr->value.type->id == ZigTypeIdMetaType) {
|
|
ZigType *err_set_type = ir_resolve_type(ira, case_expr);
|
|
if (type_is_invalid(err_set_type))
|
|
return ira->codegen->invalid_instruction;
|
|
populate_error_set_table(errors, err_set_type);
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
ZigList<ErrorTableEntry *> result_list = {};
|
|
|
|
ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet);
|
|
buf_resize(&err_set_type->name, 0);
|
|
buf_appendf(&err_set_type->name, "error{");
|
|
|
|
// Look at all the errors in the type switched on and add them to the result_list
|
|
// if they are not handled by cases.
|
|
for (uint32_t i = 0; i < target_type->data.error_set.err_count; i += 1) {
|
|
ErrorTableEntry *error_entry = target_type->data.error_set.errors[i];
|
|
ErrorTableEntry *existing_entry = errors[error_entry->value];
|
|
if (existing_entry == nullptr) {
|
|
result_list.append(error_entry);
|
|
buf_appendf(&err_set_type->name, "%s,", buf_ptr(&error_entry->name));
|
|
}
|
|
}
|
|
free(errors);
|
|
|
|
err_set_type->data.error_set.err_count = result_list.length;
|
|
err_set_type->data.error_set.errors = result_list.items;
|
|
err_set_type->size_in_bits = ira->codegen->builtin_types.entry_global_error_set->size_in_bits;
|
|
err_set_type->abi_align = ira->codegen->builtin_types.entry_global_error_set->abi_align;
|
|
err_set_type->abi_size = ira->codegen->builtin_types.entry_global_error_set->abi_size;
|
|
|
|
buf_appendf(&err_set_type->name, "}");
|
|
|
|
ZigType *new_target_value_ptr_type = get_pointer_to_type_extra(ira->codegen,
|
|
err_set_type,
|
|
ref_type->data.pointer.is_const, ref_type->data.pointer.is_volatile,
|
|
ref_type->data.pointer.ptr_len,
|
|
ref_type->data.pointer.explicit_alignment,
|
|
ref_type->data.pointer.bit_offset_in_host, ref_type->data.pointer.host_int_bytes,
|
|
ref_type->data.pointer.allow_zero);
|
|
return ir_analyze_ptr_cast(ira, &instruction->base, target_value_ptr, new_target_value_ptr_type,
|
|
&instruction->base, false);
|
|
}
|
|
|
|
return target_value_ptr;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_union_tag(IrAnalyze *ira, IrInstructionUnionTag *instruction) {
|
|
IrInstruction *value = instruction->value->child;
|
|
return ir_analyze_union_tag(ira, &instruction->base, value);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_import(IrAnalyze *ira, IrInstructionImport *import_instruction) {
|
|
Error err;
|
|
|
|
IrInstruction *name_value = import_instruction->name->child;
|
|
Buf *import_target_str = ir_resolve_str(ira, name_value);
|
|
if (!import_target_str)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
AstNode *source_node = import_instruction->base.source_node;
|
|
ZigType *import = source_node->owner;
|
|
|
|
Buf *import_target_path;
|
|
Buf *search_dir;
|
|
assert(import->data.structure.root_struct->package);
|
|
ZigPackage *target_package;
|
|
auto package_entry = import->data.structure.root_struct->package->package_table.maybe_get(import_target_str);
|
|
SourceKind source_kind;
|
|
if (package_entry) {
|
|
target_package = package_entry->value;
|
|
import_target_path = &target_package->root_src_path;
|
|
search_dir = &target_package->root_src_dir;
|
|
source_kind = SourceKindPkgMain;
|
|
} else {
|
|
// try it as a filename
|
|
target_package = import->data.structure.root_struct->package;
|
|
import_target_path = import_target_str;
|
|
|
|
// search relative to importing file
|
|
search_dir = buf_alloc();
|
|
os_path_dirname(import->data.structure.root_struct->path, search_dir);
|
|
|
|
source_kind = SourceKindNonRoot;
|
|
}
|
|
|
|
Buf full_path = BUF_INIT;
|
|
os_path_join(search_dir, import_target_path, &full_path);
|
|
|
|
Buf *import_code = buf_alloc();
|
|
Buf *resolved_path = buf_alloc();
|
|
|
|
Buf *resolve_paths[] = { &full_path, };
|
|
*resolved_path = os_path_resolve(resolve_paths, 1);
|
|
|
|
auto import_entry = ira->codegen->import_table.maybe_get(resolved_path);
|
|
if (import_entry) {
|
|
return ir_const_type(ira, &import_instruction->base, import_entry->value);
|
|
}
|
|
|
|
if (source_kind == SourceKindNonRoot) {
|
|
ZigPackage *cur_scope_pkg = scope_package(import_instruction->base.scope);
|
|
Buf *pkg_root_src_dir = &cur_scope_pkg->root_src_dir;
|
|
Buf resolved_root_src_dir = os_path_resolve(&pkg_root_src_dir, 1);
|
|
if (!buf_starts_with_buf(resolved_path, &resolved_root_src_dir)) {
|
|
ir_add_error_node(ira, source_node,
|
|
buf_sprintf("import of file outside package path: '%s'",
|
|
buf_ptr(import_target_path)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
|
|
if ((err = file_fetch(ira->codegen, resolved_path, import_code))) {
|
|
if (err == ErrorFileNotFound) {
|
|
ir_add_error_node(ira, source_node,
|
|
buf_sprintf("unable to find '%s'", buf_ptr(import_target_path)));
|
|
return ira->codegen->invalid_instruction;
|
|
} else {
|
|
ir_add_error_node(ira, source_node,
|
|
buf_sprintf("unable to open '%s': %s", buf_ptr(&full_path), err_str(err)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
|
|
ZigType *target_import = add_source_file(ira->codegen, target_package, resolved_path, import_code, source_kind);
|
|
|
|
return ir_const_type(ira, &import_instruction->base, target_import);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_ref(IrAnalyze *ira, IrInstructionRef *ref_instruction) {
|
|
IrInstruction *value = ref_instruction->value->child;
|
|
if (type_is_invalid(value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
return ir_get_ref(ira, &ref_instruction->base, value, ref_instruction->is_const, ref_instruction->is_volatile);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_union_init(IrAnalyze *ira, IrInstruction *source_instruction,
|
|
AstNode *field_source_node, ZigType *union_type, Buf *field_name, IrInstruction *field_result_loc,
|
|
IrInstruction *result_loc)
|
|
{
|
|
Error err;
|
|
assert(union_type->id == ZigTypeIdUnion);
|
|
|
|
if ((err = type_resolve(ira->codegen, union_type, ResolveStatusSizeKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
TypeUnionField *type_field = find_union_type_field(union_type, field_name);
|
|
if (type_field == nullptr) {
|
|
ir_add_error_node(ira, field_source_node,
|
|
buf_sprintf("no member named '%s' in union '%s'",
|
|
buf_ptr(field_name), buf_ptr(&union_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (type_is_invalid(type_field->type_entry))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (result_loc->value.data.x_ptr.mut == ConstPtrMutInfer) {
|
|
if (instr_is_comptime(field_result_loc) &&
|
|
field_result_loc->value.data.x_ptr.mut != ConstPtrMutRuntimeVar)
|
|
{
|
|
// nothing
|
|
} else {
|
|
result_loc->value.special = ConstValSpecialRuntime;
|
|
}
|
|
}
|
|
|
|
bool is_comptime = ir_should_inline(ira->new_irb.exec, source_instruction->scope)
|
|
|| type_requires_comptime(ira->codegen, union_type) == ReqCompTimeYes;
|
|
|
|
IrInstruction *result = ir_get_deref(ira, source_instruction, result_loc, nullptr);
|
|
if (is_comptime && !instr_is_comptime(result)) {
|
|
ir_add_error(ira, field_result_loc,
|
|
buf_sprintf("unable to evaluate constant expression"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruction *instruction,
|
|
ZigType *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields,
|
|
IrInstruction *result_loc)
|
|
{
|
|
Error err;
|
|
if (container_type->id == ZigTypeIdUnion) {
|
|
if (instr_field_count != 1) {
|
|
ir_add_error(ira, instruction,
|
|
buf_sprintf("union initialization expects exactly one field"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
IrInstructionContainerInitFieldsField *field = &fields[0];
|
|
IrInstruction *field_result_loc = field->result_loc->child;
|
|
if (type_is_invalid(field_result_loc->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
return ir_analyze_union_init(ira, instruction, field->source_node, container_type, field->name,
|
|
field_result_loc, result_loc);
|
|
}
|
|
if (container_type->id != ZigTypeIdStruct || is_slice(container_type)) {
|
|
ir_add_error(ira, instruction,
|
|
buf_sprintf("type '%s' does not support struct initialization syntax",
|
|
buf_ptr(&container_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if ((err = type_resolve(ira->codegen, container_type, ResolveStatusSizeKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
size_t actual_field_count = container_type->data.structure.src_field_count;
|
|
|
|
IrInstruction *first_non_const_instruction = nullptr;
|
|
|
|
AstNode **field_assign_nodes = allocate<AstNode *>(actual_field_count);
|
|
ZigList<IrInstruction *> const_ptrs = {};
|
|
|
|
bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope)
|
|
|| type_requires_comptime(ira->codegen, container_type) == ReqCompTimeYes;
|
|
|
|
|
|
// Here we iterate over the fields that have been initialized, and emit
|
|
// compile errors for missing fields and duplicate fields.
|
|
// It is only now that we find out whether the struct initialization can be a comptime
|
|
// value, but we have already emitted runtime instructions for the fields that
|
|
// were initialized with runtime values, and have omitted instructions that would have
|
|
// initialized fields with comptime values.
|
|
// So now we must clean up this situation. If it turns out the struct initialization can
|
|
// be a comptime value, overwrite ConstPtrMutInfer with ConstPtrMutComptimeConst.
|
|
// Otherwise, we must emit instructions to runtime-initialize the fields that have
|
|
// comptime-known values.
|
|
|
|
for (size_t i = 0; i < instr_field_count; i += 1) {
|
|
IrInstructionContainerInitFieldsField *field = &fields[i];
|
|
|
|
IrInstruction *field_result_loc = field->result_loc->child;
|
|
if (type_is_invalid(field_result_loc->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
TypeStructField *type_field = find_struct_type_field(container_type, field->name);
|
|
if (!type_field) {
|
|
ir_add_error_node(ira, field->source_node,
|
|
buf_sprintf("no member named '%s' in struct '%s'",
|
|
buf_ptr(field->name), buf_ptr(&container_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (type_is_invalid(type_field->type_entry))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
size_t field_index = type_field->src_index;
|
|
AstNode *existing_assign_node = field_assign_nodes[field_index];
|
|
if (existing_assign_node) {
|
|
ErrorMsg *msg = ir_add_error_node(ira, field->source_node, buf_sprintf("duplicate field"));
|
|
add_error_note(ira->codegen, msg, existing_assign_node, buf_sprintf("other field here"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
field_assign_nodes[field_index] = field->source_node;
|
|
|
|
if (instr_is_comptime(field_result_loc) &&
|
|
field_result_loc->value.data.x_ptr.mut != ConstPtrMutRuntimeVar)
|
|
{
|
|
const_ptrs.append(field_result_loc);
|
|
} else {
|
|
first_non_const_instruction = field_result_loc;
|
|
}
|
|
}
|
|
|
|
bool any_missing = false;
|
|
for (size_t i = 0; i < actual_field_count; i += 1) {
|
|
if (field_assign_nodes[i] != nullptr) continue;
|
|
|
|
// look for a default field value
|
|
TypeStructField *field = &container_type->data.structure.fields[i];
|
|
if (field->init_val == nullptr) {
|
|
// it's not memoized. time to go analyze it
|
|
assert(field->decl_node->type == NodeTypeStructField);
|
|
AstNode *init_node = field->decl_node->data.struct_field.value;
|
|
if (init_node == nullptr) {
|
|
ir_add_error_node(ira, instruction->source_node,
|
|
buf_sprintf("missing field: '%s'", buf_ptr(container_type->data.structure.fields[i].name)));
|
|
any_missing = true;
|
|
continue;
|
|
}
|
|
// scope is not the scope of the struct init, it's the scope of the struct type decl
|
|
Scope *analyze_scope = &get_container_scope(container_type)->base;
|
|
// memoize it
|
|
field->init_val = analyze_const_value(ira->codegen, analyze_scope, init_node,
|
|
field->type_entry, nullptr);
|
|
}
|
|
if (type_is_invalid(field->init_val->type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *runtime_inst = ir_const(ira, instruction, field->init_val->type);
|
|
copy_const_val(&runtime_inst->value, field->init_val, true);
|
|
|
|
IrInstruction *field_ptr = ir_analyze_struct_field_ptr(ira, instruction, field, result_loc,
|
|
container_type, true);
|
|
ir_analyze_store_ptr(ira, instruction, field_ptr, runtime_inst, false);
|
|
if (instr_is_comptime(field_ptr) && field_ptr->value.data.x_ptr.mut != ConstPtrMutRuntimeVar) {
|
|
const_ptrs.append(field_ptr);
|
|
} else {
|
|
first_non_const_instruction = result_loc;
|
|
}
|
|
}
|
|
if (any_missing)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (result_loc->value.data.x_ptr.mut == ConstPtrMutInfer) {
|
|
if (const_ptrs.length != actual_field_count) {
|
|
result_loc->value.special = ConstValSpecialRuntime;
|
|
for (size_t i = 0; i < const_ptrs.length; i += 1) {
|
|
IrInstruction *field_result_loc = const_ptrs.at(i);
|
|
IrInstruction *deref = ir_get_deref(ira, field_result_loc, field_result_loc, nullptr);
|
|
field_result_loc->value.special = ConstValSpecialRuntime;
|
|
ir_analyze_store_ptr(ira, field_result_loc, field_result_loc, deref, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
IrInstruction *result = ir_get_deref(ira, instruction, result_loc, nullptr);
|
|
|
|
if (is_comptime && !instr_is_comptime(result)) {
|
|
ir_add_error_node(ira, first_non_const_instruction->source_node,
|
|
buf_sprintf("unable to evaluate constant expression"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira,
|
|
IrInstructionContainerInitList *instruction)
|
|
{
|
|
ZigType *container_type = ir_resolve_type(ira, instruction->container_type->child);
|
|
if (type_is_invalid(container_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
size_t elem_count = instruction->item_count;
|
|
|
|
if (is_slice(container_type)) {
|
|
ir_add_error(ira, instruction->container_type,
|
|
buf_sprintf("expected array type or [_], found slice"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (container_type->id == ZigTypeIdVoid) {
|
|
if (elem_count != 0) {
|
|
ir_add_error_node(ira, instruction->base.source_node,
|
|
buf_sprintf("void expression expects no arguments"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
return ir_const_void(ira, &instruction->base);
|
|
}
|
|
|
|
if (container_type->id == ZigTypeIdStruct && elem_count == 0) {
|
|
ir_assert(instruction->result_loc != nullptr, &instruction->base);
|
|
IrInstruction *result_loc = instruction->result_loc->child;
|
|
if (type_is_invalid(result_loc->value.type))
|
|
return result_loc;
|
|
return ir_analyze_container_init_fields(ira, &instruction->base, container_type, 0, nullptr, result_loc);
|
|
}
|
|
|
|
if (container_type->id != ZigTypeIdArray) {
|
|
ir_add_error_node(ira, instruction->base.source_node,
|
|
buf_sprintf("type '%s' does not support array initialization",
|
|
buf_ptr(&container_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
ir_assert(instruction->result_loc != nullptr, &instruction->base);
|
|
IrInstruction *result_loc = instruction->result_loc->child;
|
|
if (type_is_invalid(result_loc->value.type))
|
|
return result_loc;
|
|
ir_assert(result_loc->value.type->id == ZigTypeIdPointer, &instruction->base);
|
|
|
|
ZigType *child_type = container_type->data.array.child_type;
|
|
if (container_type->data.array.len != elem_count) {
|
|
ZigType *literal_type = get_array_type(ira->codegen, child_type, elem_count);
|
|
|
|
ir_add_error(ira, &instruction->base,
|
|
buf_sprintf("expected %s literal, found %s literal",
|
|
buf_ptr(&container_type->name), buf_ptr(&literal_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
switch (type_has_one_possible_value(ira->codegen, container_type)) {
|
|
case OnePossibleValueInvalid:
|
|
return ira->codegen->invalid_instruction;
|
|
case OnePossibleValueYes:
|
|
return ir_const(ira, &instruction->base, container_type);
|
|
case OnePossibleValueNo:
|
|
break;
|
|
}
|
|
|
|
bool is_comptime;
|
|
switch (type_requires_comptime(ira->codegen, container_type)) {
|
|
case ReqCompTimeInvalid:
|
|
return ira->codegen->invalid_instruction;
|
|
case ReqCompTimeNo:
|
|
is_comptime = ir_should_inline(ira->new_irb.exec, instruction->base.scope);
|
|
break;
|
|
case ReqCompTimeYes:
|
|
is_comptime = true;
|
|
break;
|
|
}
|
|
|
|
IrInstruction *first_non_const_instruction = nullptr;
|
|
|
|
// The Result Location Mechanism has already emitted runtime instructions to
|
|
// initialize runtime elements and has omitted instructions for the comptime
|
|
// elements. However it is only now that we find out whether the array initialization
|
|
// can be a comptime value. So we must clean up the situation. If it turns out
|
|
// array initialization can be a comptime value, overwrite ConstPtrMutInfer with
|
|
// ConstPtrMutComptimeConst. Otherwise, emit instructions to runtime-initialize the
|
|
// elements that have comptime-known values.
|
|
ZigList<IrInstruction *> const_ptrs = {};
|
|
|
|
for (size_t i = 0; i < elem_count; i += 1) {
|
|
IrInstruction *elem_result_loc = instruction->elem_result_loc_list[i]->child;
|
|
if (type_is_invalid(elem_result_loc->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
assert(elem_result_loc->value.type->id == ZigTypeIdPointer);
|
|
|
|
if (instr_is_comptime(elem_result_loc) &&
|
|
elem_result_loc->value.data.x_ptr.mut != ConstPtrMutRuntimeVar)
|
|
{
|
|
const_ptrs.append(elem_result_loc);
|
|
} else {
|
|
first_non_const_instruction = elem_result_loc;
|
|
}
|
|
}
|
|
|
|
if (result_loc->value.data.x_ptr.mut == ConstPtrMutInfer) {
|
|
if (const_ptrs.length != elem_count) {
|
|
result_loc->value.special = ConstValSpecialRuntime;
|
|
for (size_t i = 0; i < const_ptrs.length; i += 1) {
|
|
IrInstruction *elem_result_loc = const_ptrs.at(i);
|
|
assert(elem_result_loc->value.special == ConstValSpecialStatic);
|
|
IrInstruction *deref = ir_get_deref(ira, elem_result_loc, elem_result_loc, nullptr);
|
|
elem_result_loc->value.special = ConstValSpecialRuntime;
|
|
ir_analyze_store_ptr(ira, elem_result_loc, elem_result_loc, deref, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
IrInstruction *result = ir_get_deref(ira, &instruction->base, result_loc, nullptr);
|
|
if (instr_is_comptime(result))
|
|
return result;
|
|
|
|
if (is_comptime) {
|
|
ir_add_error_node(ira, first_non_const_instruction->source_node,
|
|
buf_sprintf("unable to evaluate constant expression"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
ZigType *result_elem_type = result_loc->value.type->data.pointer.child_type;
|
|
if (is_slice(result_elem_type)) {
|
|
ErrorMsg *msg = ir_add_error(ira, &instruction->base,
|
|
buf_sprintf("runtime-initialized array cannot be casted to slice type '%s'",
|
|
buf_ptr(&result_elem_type->name)));
|
|
add_error_note(ira->codegen, msg, first_non_const_instruction->source_node,
|
|
buf_sprintf("this value is not comptime-known"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_container_init_fields(IrAnalyze *ira,
|
|
IrInstructionContainerInitFields *instruction)
|
|
{
|
|
IrInstruction *container_type_value = instruction->container_type->child;
|
|
ZigType *container_type = ir_resolve_type(ira, container_type_value);
|
|
if (type_is_invalid(container_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ir_assert(instruction->result_loc != nullptr, &instruction->base);
|
|
IrInstruction *result_loc = instruction->result_loc->child;
|
|
if (type_is_invalid(result_loc->value.type))
|
|
return result_loc;
|
|
|
|
return ir_analyze_container_init_fields(ira, &instruction->base, container_type,
|
|
instruction->field_count, instruction->fields, result_loc);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_compile_err(IrAnalyze *ira,
|
|
IrInstructionCompileErr *instruction)
|
|
{
|
|
IrInstruction *msg_value = instruction->msg->child;
|
|
Buf *msg_buf = ir_resolve_str(ira, msg_value);
|
|
if (!msg_buf)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ErrorMsg *msg = ir_add_error(ira, &instruction->base, msg_buf);
|
|
emit_error_notes_for_ref_stack(ira->codegen, msg);
|
|
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_compile_log(IrAnalyze *ira, IrInstructionCompileLog *instruction) {
|
|
Buf buf = BUF_INIT;
|
|
fprintf(stderr, "| ");
|
|
for (size_t i = 0; i < instruction->msg_count; i += 1) {
|
|
IrInstruction *msg = instruction->msg_list[i]->child;
|
|
if (type_is_invalid(msg->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
buf_resize(&buf, 0);
|
|
render_const_value(ira->codegen, &buf, &msg->value);
|
|
const char *comma_str = (i != 0) ? ", " : "";
|
|
fprintf(stderr, "%s%s", comma_str, buf_ptr(&buf));
|
|
}
|
|
fprintf(stderr, "\n");
|
|
|
|
auto *expr = &instruction->base.source_node->data.fn_call_expr;
|
|
if (!expr->seen) {
|
|
// Here we bypass higher level functions such as ir_add_error because we do not want
|
|
// invalidate_exec to be called.
|
|
add_node_error(ira->codegen, instruction->base.source_node, buf_sprintf("found compile log statement"));
|
|
}
|
|
expr->seen = true;
|
|
|
|
return ir_const_void(ira, &instruction->base);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstructionErrName *instruction) {
|
|
IrInstruction *value = instruction->value->child;
|
|
if (type_is_invalid(value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->codegen->builtin_types.entry_global_error_set);
|
|
if (type_is_invalid(casted_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *u8_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,
|
|
true, false, PtrLenUnknown, 0, 0, 0, false);
|
|
ZigType *str_type = get_slice_type(ira->codegen, u8_ptr_type);
|
|
if (casted_value->value.special == ConstValSpecialStatic) {
|
|
ErrorTableEntry *err = casted_value->value.data.x_err_set;
|
|
if (!err->cached_error_name_val) {
|
|
ConstExprValue *array_val = create_const_str_lit(ira->codegen, &err->name);
|
|
err->cached_error_name_val = create_const_slice(ira->codegen, array_val, 0, buf_len(&err->name), true);
|
|
}
|
|
IrInstruction *result = ir_const(ira, &instruction->base, nullptr);
|
|
copy_const_val(&result->value, err->cached_error_name_val, true);
|
|
result->value.type = str_type;
|
|
return result;
|
|
}
|
|
|
|
ira->codegen->generate_error_name_table = true;
|
|
|
|
IrInstruction *result = ir_build_err_name(&ira->new_irb,
|
|
instruction->base.scope, instruction->base.source_node, value);
|
|
result->value.type = str_type;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrInstructionTagName *instruction) {
|
|
Error err;
|
|
IrInstruction *target = instruction->target->child;
|
|
if (type_is_invalid(target->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
assert(target->value.type->id == ZigTypeIdEnum);
|
|
|
|
if (instr_is_comptime(target)) {
|
|
if ((err = type_resolve(ira->codegen, target->value.type, ResolveStatusZeroBitsKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
TypeEnumField *field = find_enum_field_by_tag(target->value.type, &target->value.data.x_bigint);
|
|
ConstExprValue *array_val = create_const_str_lit(ira->codegen, field->name);
|
|
IrInstruction *result = ir_const(ira, &instruction->base, nullptr);
|
|
init_const_slice(ira->codegen, &result->value, array_val, 0, buf_len(field->name), true);
|
|
return result;
|
|
}
|
|
|
|
IrInstruction *result = ir_build_tag_name(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node, target);
|
|
ZigType *u8_ptr_type = get_pointer_to_type_extra(
|
|
ira->codegen, ira->codegen->builtin_types.entry_u8,
|
|
true, false, PtrLenUnknown,
|
|
0, 0, 0, false);
|
|
result->value.type = get_slice_type(ira->codegen, u8_ptr_type);
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira,
|
|
IrInstructionFieldParentPtr *instruction)
|
|
{
|
|
Error err;
|
|
IrInstruction *type_value = instruction->type_value->child;
|
|
ZigType *container_type = ir_resolve_type(ira, type_value);
|
|
if (type_is_invalid(container_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *field_name_value = instruction->field_name->child;
|
|
Buf *field_name = ir_resolve_str(ira, field_name_value);
|
|
if (!field_name)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *field_ptr = instruction->field_ptr->child;
|
|
if (type_is_invalid(field_ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (container_type->id != ZigTypeIdStruct) {
|
|
ir_add_error(ira, type_value,
|
|
buf_sprintf("expected struct type, found '%s'", buf_ptr(&container_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if ((err = ensure_complete_type(ira->codegen, container_type)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
TypeStructField *field = find_struct_type_field(container_type, field_name);
|
|
if (field == nullptr) {
|
|
ir_add_error(ira, field_name_value,
|
|
buf_sprintf("struct '%s' has no field '%s'",
|
|
buf_ptr(&container_type->name), buf_ptr(field_name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (field_ptr->value.type->id != ZigTypeIdPointer) {
|
|
ir_add_error(ira, field_ptr,
|
|
buf_sprintf("expected pointer, found '%s'", buf_ptr(&field_ptr->value.type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
bool is_packed = (container_type->data.structure.layout == ContainerLayoutPacked);
|
|
uint32_t field_ptr_align = is_packed ? 1 : get_abi_alignment(ira->codegen, field->type_entry);
|
|
uint32_t parent_ptr_align = is_packed ? 1 : get_abi_alignment(ira->codegen, container_type);
|
|
|
|
ZigType *field_ptr_type = get_pointer_to_type_extra(ira->codegen, field->type_entry,
|
|
field_ptr->value.type->data.pointer.is_const,
|
|
field_ptr->value.type->data.pointer.is_volatile,
|
|
PtrLenSingle,
|
|
field_ptr_align, 0, 0, false);
|
|
IrInstruction *casted_field_ptr = ir_implicit_cast(ira, field_ptr, field_ptr_type);
|
|
if (type_is_invalid(casted_field_ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *result_type = get_pointer_to_type_extra(ira->codegen, container_type,
|
|
casted_field_ptr->value.type->data.pointer.is_const,
|
|
casted_field_ptr->value.type->data.pointer.is_volatile,
|
|
PtrLenSingle,
|
|
parent_ptr_align, 0, 0, false);
|
|
|
|
if (instr_is_comptime(casted_field_ptr)) {
|
|
ConstExprValue *field_ptr_val = ir_resolve_const(ira, casted_field_ptr, UndefBad);
|
|
if (!field_ptr_val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (field_ptr_val->data.x_ptr.special != ConstPtrSpecialBaseStruct) {
|
|
ir_add_error(ira, field_ptr, buf_sprintf("pointer value not based on parent struct"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
size_t ptr_field_index = field_ptr_val->data.x_ptr.data.base_struct.field_index;
|
|
if (ptr_field_index != field->src_index) {
|
|
ir_add_error(ira, &instruction->base,
|
|
buf_sprintf("field '%s' has index %" ZIG_PRI_usize " but pointer value is index %" ZIG_PRI_usize " of struct '%s'",
|
|
buf_ptr(field->name), field->src_index,
|
|
ptr_field_index, buf_ptr(&container_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *result = ir_const(ira, &instruction->base, result_type);
|
|
ConstExprValue *out_val = &result->value;
|
|
out_val->data.x_ptr.special = ConstPtrSpecialRef;
|
|
out_val->data.x_ptr.data.ref.pointee = field_ptr_val->data.x_ptr.data.base_struct.struct_val;
|
|
out_val->data.x_ptr.mut = field_ptr_val->data.x_ptr.mut;
|
|
return result;
|
|
}
|
|
|
|
IrInstruction *result = ir_build_field_parent_ptr(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node, type_value, field_name_value, casted_field_ptr, field);
|
|
result->value.type = result_type;
|
|
return result;
|
|
}
|
|
|
|
static TypeStructField *validate_byte_offset(IrAnalyze *ira,
|
|
IrInstruction *type_value,
|
|
IrInstruction *field_name_value,
|
|
size_t *byte_offset)
|
|
{
|
|
ZigType *container_type = ir_resolve_type(ira, type_value);
|
|
if (type_is_invalid(container_type))
|
|
return nullptr;
|
|
|
|
Error err;
|
|
if ((err = ensure_complete_type(ira->codegen, container_type)))
|
|
return nullptr;
|
|
|
|
Buf *field_name = ir_resolve_str(ira, field_name_value);
|
|
if (!field_name)
|
|
return nullptr;
|
|
|
|
if (container_type->id != ZigTypeIdStruct) {
|
|
ir_add_error(ira, type_value,
|
|
buf_sprintf("expected struct type, found '%s'", buf_ptr(&container_type->name)));
|
|
return nullptr;
|
|
}
|
|
|
|
TypeStructField *field = find_struct_type_field(container_type, field_name);
|
|
if (field == nullptr) {
|
|
ir_add_error(ira, field_name_value,
|
|
buf_sprintf("struct '%s' has no field '%s'",
|
|
buf_ptr(&container_type->name), buf_ptr(field_name)));
|
|
return nullptr;
|
|
}
|
|
|
|
if (!type_has_bits(field->type_entry)) {
|
|
ir_add_error(ira, field_name_value,
|
|
buf_sprintf("zero-bit field '%s' in struct '%s' has no offset",
|
|
buf_ptr(field_name), buf_ptr(&container_type->name)));
|
|
return nullptr;
|
|
}
|
|
|
|
*byte_offset = field->offset;
|
|
return field;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_byte_offset_of(IrAnalyze *ira,
|
|
IrInstructionByteOffsetOf *instruction)
|
|
{
|
|
IrInstruction *type_value = instruction->type_value->child;
|
|
if (type_is_invalid(type_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *field_name_value = instruction->field_name->child;
|
|
size_t byte_offset = 0;
|
|
if (!validate_byte_offset(ira, type_value, field_name_value, &byte_offset))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
|
|
return ir_const_unsigned(ira, &instruction->base, byte_offset);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_bit_offset_of(IrAnalyze *ira,
|
|
IrInstructionBitOffsetOf *instruction)
|
|
{
|
|
IrInstruction *type_value = instruction->type_value->child;
|
|
if (type_is_invalid(type_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
IrInstruction *field_name_value = instruction->field_name->child;
|
|
size_t byte_offset = 0;
|
|
TypeStructField *field = nullptr;
|
|
if (!(field = validate_byte_offset(ira, type_value, field_name_value, &byte_offset)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
size_t bit_offset = byte_offset * 8 + field->bit_offset_in_host;
|
|
return ir_const_unsigned(ira, &instruction->base, bit_offset);
|
|
}
|
|
|
|
static void ensure_field_index(ZigType *type, const char *field_name, size_t index) {
|
|
Buf *field_name_buf;
|
|
|
|
assert(type != nullptr && !type_is_invalid(type));
|
|
// Check for our field by creating a buffer in place then using the comma operator to free it so that we don't
|
|
// leak memory in debug mode.
|
|
assert(find_struct_type_field(type, field_name_buf = buf_create_from_str(field_name))->src_index == index &&
|
|
(buf_deinit(field_name_buf), true));
|
|
}
|
|
|
|
static ZigType *ir_type_info_get_type(IrAnalyze *ira, const char *type_name, ZigType *root) {
|
|
Error err;
|
|
ConstExprValue *type_info_var = get_builtin_value(ira->codegen, "TypeInfo");
|
|
assert(type_info_var->type->id == ZigTypeIdMetaType);
|
|
ZigType *type_info_type = type_info_var->data.x_type;
|
|
assert(type_info_type->id == ZigTypeIdUnion);
|
|
if ((err = type_resolve(ira->codegen, type_info_type, ResolveStatusSizeKnown))) {
|
|
zig_unreachable();
|
|
}
|
|
|
|
if (type_name == nullptr && root == nullptr)
|
|
return type_info_type;
|
|
else if (type_name == nullptr)
|
|
return root;
|
|
|
|
ZigType *root_type = (root == nullptr) ? type_info_type : root;
|
|
|
|
ScopeDecls *type_info_scope = get_container_scope(root_type);
|
|
assert(type_info_scope != nullptr);
|
|
|
|
Buf field_name = BUF_INIT;
|
|
buf_init_from_str(&field_name, type_name);
|
|
auto entry = type_info_scope->decl_table.get(&field_name);
|
|
buf_deinit(&field_name);
|
|
|
|
TldVar *tld = (TldVar *)entry;
|
|
assert(tld->base.id == TldIdVar);
|
|
|
|
ZigVar *var = tld->var;
|
|
|
|
if ((err = ensure_complete_type(ira->codegen, var->const_value->type)))
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
|
|
assert(var->const_value->type->id == ZigTypeIdMetaType);
|
|
return var->const_value->data.x_type;
|
|
}
|
|
|
|
static Error ir_make_type_info_decls(IrAnalyze *ira, IrInstruction *source_instr, ConstExprValue *out_val,
|
|
ScopeDecls *decls_scope)
|
|
{
|
|
Error err;
|
|
ZigType *type_info_declaration_type = ir_type_info_get_type(ira, "Declaration", nullptr);
|
|
if ((err = type_resolve(ira->codegen, type_info_declaration_type, ResolveStatusSizeKnown)))
|
|
return err;
|
|
|
|
ensure_field_index(type_info_declaration_type, "name", 0);
|
|
ensure_field_index(type_info_declaration_type, "is_pub", 1);
|
|
ensure_field_index(type_info_declaration_type, "data", 2);
|
|
|
|
ZigType *type_info_declaration_data_type = ir_type_info_get_type(ira, "Data", type_info_declaration_type);
|
|
if ((err = ensure_complete_type(ira->codegen, type_info_declaration_data_type)))
|
|
return err;
|
|
|
|
ZigType *type_info_fn_decl_type = ir_type_info_get_type(ira, "FnDecl", type_info_declaration_data_type);
|
|
if ((err = ensure_complete_type(ira->codegen, type_info_fn_decl_type)))
|
|
return err;
|
|
|
|
ZigType *type_info_fn_decl_inline_type = ir_type_info_get_type(ira, "Inline", type_info_fn_decl_type);
|
|
if ((err = ensure_complete_type(ira->codegen, type_info_fn_decl_inline_type)))
|
|
return err;
|
|
|
|
// Loop through our declarations once to figure out how many declarations we will generate info for.
|
|
auto decl_it = decls_scope->decl_table.entry_iterator();
|
|
decltype(decls_scope->decl_table)::Entry *curr_entry = nullptr;
|
|
int declaration_count = 0;
|
|
|
|
while ((curr_entry = decl_it.next()) != nullptr) {
|
|
// If the declaration is unresolved, force it to be resolved again.
|
|
if (curr_entry->value->resolution == TldResolutionUnresolved) {
|
|
resolve_top_level_decl(ira->codegen, curr_entry->value, curr_entry->value->source_node);
|
|
if (curr_entry->value->resolution != TldResolutionOk) {
|
|
return ErrorSemanticAnalyzeFail;
|
|
}
|
|
}
|
|
|
|
// Skip comptime blocks and test functions.
|
|
if (curr_entry->value->id != TldIdCompTime) {
|
|
if (curr_entry->value->id == TldIdFn) {
|
|
ZigFn *fn_entry = ((TldFn *)curr_entry->value)->fn_entry;
|
|
if (fn_entry->is_test)
|
|
continue;
|
|
}
|
|
|
|
declaration_count += 1;
|
|
}
|
|
}
|
|
|
|
ConstExprValue *declaration_array = create_const_vals(1);
|
|
declaration_array->special = ConstValSpecialStatic;
|
|
declaration_array->type = get_array_type(ira->codegen, type_info_declaration_type, declaration_count);
|
|
declaration_array->data.x_array.special = ConstArraySpecialNone;
|
|
declaration_array->data.x_array.data.s_none.elements = create_const_vals(declaration_count);
|
|
init_const_slice(ira->codegen, out_val, declaration_array, 0, declaration_count, false);
|
|
|
|
// Loop through the declarations and generate info.
|
|
decl_it = decls_scope->decl_table.entry_iterator();
|
|
curr_entry = nullptr;
|
|
int declaration_index = 0;
|
|
while ((curr_entry = decl_it.next()) != nullptr) {
|
|
// Skip comptime blocks and test functions.
|
|
if (curr_entry->value->id == TldIdCompTime) {
|
|
continue;
|
|
} else if (curr_entry->value->id == TldIdFn) {
|
|
ZigFn *fn_entry = ((TldFn *)curr_entry->value)->fn_entry;
|
|
if (fn_entry->is_test)
|
|
continue;
|
|
}
|
|
|
|
ConstExprValue *declaration_val = &declaration_array->data.x_array.data.s_none.elements[declaration_index];
|
|
|
|
declaration_val->special = ConstValSpecialStatic;
|
|
declaration_val->type = type_info_declaration_type;
|
|
|
|
ConstExprValue *inner_fields = create_const_vals(3);
|
|
ConstExprValue *name = create_const_str_lit(ira->codegen, curr_entry->key);
|
|
init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(curr_entry->key), true);
|
|
inner_fields[1].special = ConstValSpecialStatic;
|
|
inner_fields[1].type = ira->codegen->builtin_types.entry_bool;
|
|
inner_fields[1].data.x_bool = curr_entry->value->visib_mod == VisibModPub;
|
|
inner_fields[2].special = ConstValSpecialStatic;
|
|
inner_fields[2].type = type_info_declaration_data_type;
|
|
inner_fields[2].parent.id = ConstParentIdStruct;
|
|
inner_fields[2].parent.data.p_struct.struct_val = declaration_val;
|
|
inner_fields[2].parent.data.p_struct.field_index = 1;
|
|
|
|
switch (curr_entry->value->id) {
|
|
case TldIdVar:
|
|
{
|
|
ZigVar *var = ((TldVar *)curr_entry->value)->var;
|
|
if ((err = ensure_complete_type(ira->codegen, var->const_value->type)))
|
|
return ErrorSemanticAnalyzeFail;
|
|
|
|
if (var->const_value->type->id == ZigTypeIdMetaType) {
|
|
// We have a variable of type 'type', so it's actually a type declaration.
|
|
// 0: Data.Type: type
|
|
bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 0);
|
|
inner_fields[2].data.x_union.payload = var->const_value;
|
|
} else {
|
|
// We have a variable of another type, so we store the type of the variable.
|
|
// 1: Data.Var: type
|
|
bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 1);
|
|
|
|
ConstExprValue *payload = create_const_vals(1);
|
|
payload->type = ira->codegen->builtin_types.entry_type;
|
|
payload->data.x_type = var->const_value->type;
|
|
|
|
inner_fields[2].data.x_union.payload = payload;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case TldIdFn:
|
|
{
|
|
// 2: Data.Fn: Data.FnDecl
|
|
bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 2);
|
|
|
|
ZigFn *fn_entry = ((TldFn *)curr_entry->value)->fn_entry;
|
|
assert(!fn_entry->is_test);
|
|
|
|
if (fn_entry->type_entry == nullptr) {
|
|
ir_error_dependency_loop(ira, source_instr);
|
|
return ErrorSemanticAnalyzeFail;
|
|
}
|
|
|
|
AstNodeFnProto *fn_node = &fn_entry->proto_node->data.fn_proto;
|
|
|
|
ConstExprValue *fn_decl_val = create_const_vals(1);
|
|
fn_decl_val->special = ConstValSpecialStatic;
|
|
fn_decl_val->type = type_info_fn_decl_type;
|
|
fn_decl_val->parent.id = ConstParentIdUnion;
|
|
fn_decl_val->parent.data.p_union.union_val = &inner_fields[2];
|
|
|
|
ConstExprValue *fn_decl_fields = create_const_vals(9);
|
|
fn_decl_val->data.x_struct.fields = fn_decl_fields;
|
|
|
|
// fn_type: type
|
|
ensure_field_index(fn_decl_val->type, "fn_type", 0);
|
|
fn_decl_fields[0].special = ConstValSpecialStatic;
|
|
fn_decl_fields[0].type = ira->codegen->builtin_types.entry_type;
|
|
fn_decl_fields[0].data.x_type = fn_entry->type_entry;
|
|
// inline_type: Data.FnDecl.Inline
|
|
ensure_field_index(fn_decl_val->type, "inline_type", 1);
|
|
fn_decl_fields[1].special = ConstValSpecialStatic;
|
|
fn_decl_fields[1].type = type_info_fn_decl_inline_type;
|
|
bigint_init_unsigned(&fn_decl_fields[1].data.x_enum_tag, fn_entry->fn_inline);
|
|
// calling_convention: TypeInfo.CallingConvention
|
|
ensure_field_index(fn_decl_val->type, "calling_convention", 2);
|
|
fn_decl_fields[2].special = ConstValSpecialStatic;
|
|
fn_decl_fields[2].type = ir_type_info_get_type(ira, "CallingConvention", nullptr);
|
|
bigint_init_unsigned(&fn_decl_fields[2].data.x_enum_tag, fn_node->cc);
|
|
// is_var_args: bool
|
|
ensure_field_index(fn_decl_val->type, "is_var_args", 3);
|
|
bool is_varargs = fn_node->is_var_args;
|
|
fn_decl_fields[3].special = ConstValSpecialStatic;
|
|
fn_decl_fields[3].type = ira->codegen->builtin_types.entry_bool;
|
|
fn_decl_fields[3].data.x_bool = is_varargs;
|
|
// is_extern: bool
|
|
ensure_field_index(fn_decl_val->type, "is_extern", 4);
|
|
fn_decl_fields[4].special = ConstValSpecialStatic;
|
|
fn_decl_fields[4].type = ira->codegen->builtin_types.entry_bool;
|
|
fn_decl_fields[4].data.x_bool = fn_node->is_extern;
|
|
// is_export: bool
|
|
ensure_field_index(fn_decl_val->type, "is_export", 5);
|
|
fn_decl_fields[5].special = ConstValSpecialStatic;
|
|
fn_decl_fields[5].type = ira->codegen->builtin_types.entry_bool;
|
|
fn_decl_fields[5].data.x_bool = fn_node->is_export;
|
|
// lib_name: ?[]const u8
|
|
ensure_field_index(fn_decl_val->type, "lib_name", 6);
|
|
fn_decl_fields[6].special = ConstValSpecialStatic;
|
|
ZigType *u8_ptr = get_pointer_to_type_extra(
|
|
ira->codegen, ira->codegen->builtin_types.entry_u8,
|
|
true, false, PtrLenUnknown,
|
|
0, 0, 0, false);
|
|
fn_decl_fields[6].type = get_optional_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr));
|
|
if (fn_node->is_extern && fn_node->lib_name != nullptr && buf_len(fn_node->lib_name) > 0) {
|
|
fn_decl_fields[6].data.x_optional = create_const_vals(1);
|
|
ConstExprValue *lib_name = create_const_str_lit(ira->codegen, fn_node->lib_name);
|
|
init_const_slice(ira->codegen, fn_decl_fields[6].data.x_optional, lib_name, 0,
|
|
buf_len(fn_node->lib_name), true);
|
|
} else {
|
|
fn_decl_fields[6].data.x_optional = nullptr;
|
|
}
|
|
// return_type: type
|
|
ensure_field_index(fn_decl_val->type, "return_type", 7);
|
|
fn_decl_fields[7].special = ConstValSpecialStatic;
|
|
fn_decl_fields[7].type = ira->codegen->builtin_types.entry_type;
|
|
fn_decl_fields[7].data.x_type = fn_entry->type_entry->data.fn.fn_type_id.return_type;
|
|
// arg_names: [][] const u8
|
|
ensure_field_index(fn_decl_val->type, "arg_names", 8);
|
|
size_t fn_arg_count = fn_entry->variable_list.length;
|
|
ConstExprValue *fn_arg_name_array = create_const_vals(1);
|
|
fn_arg_name_array->special = ConstValSpecialStatic;
|
|
fn_arg_name_array->type = get_array_type(ira->codegen,
|
|
get_slice_type(ira->codegen, u8_ptr), fn_arg_count);
|
|
fn_arg_name_array->data.x_array.special = ConstArraySpecialNone;
|
|
fn_arg_name_array->data.x_array.data.s_none.elements = create_const_vals(fn_arg_count);
|
|
|
|
init_const_slice(ira->codegen, &fn_decl_fields[8], fn_arg_name_array, 0, fn_arg_count, false);
|
|
|
|
for (size_t fn_arg_index = 0; fn_arg_index < fn_arg_count; fn_arg_index++) {
|
|
ZigVar *arg_var = fn_entry->variable_list.at(fn_arg_index);
|
|
ConstExprValue *fn_arg_name_val = &fn_arg_name_array->data.x_array.data.s_none.elements[fn_arg_index];
|
|
ConstExprValue *arg_name = create_const_str_lit(ira->codegen, &arg_var->name);
|
|
init_const_slice(ira->codegen, fn_arg_name_val, arg_name, 0, buf_len(&arg_var->name), true);
|
|
fn_arg_name_val->parent.id = ConstParentIdArray;
|
|
fn_arg_name_val->parent.data.p_array.array_val = fn_arg_name_array;
|
|
fn_arg_name_val->parent.data.p_array.elem_index = fn_arg_index;
|
|
}
|
|
|
|
inner_fields[2].data.x_union.payload = fn_decl_val;
|
|
break;
|
|
}
|
|
case TldIdContainer:
|
|
{
|
|
ZigType *type_entry = ((TldContainer *)curr_entry->value)->type_entry;
|
|
if ((err = ensure_complete_type(ira->codegen, type_entry)))
|
|
return ErrorSemanticAnalyzeFail;
|
|
|
|
// This is a type.
|
|
bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 0);
|
|
|
|
ConstExprValue *payload = create_const_vals(1);
|
|
payload->type = ira->codegen->builtin_types.entry_type;
|
|
payload->data.x_type = type_entry;
|
|
|
|
inner_fields[2].data.x_union.payload = payload;
|
|
|
|
break;
|
|
}
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
|
|
declaration_val->data.x_struct.fields = inner_fields;
|
|
declaration_index++;
|
|
}
|
|
|
|
assert(declaration_index == declaration_count);
|
|
return ErrorNone;
|
|
}
|
|
|
|
static uint32_t ptr_len_to_size_enum_index(PtrLen ptr_len) {
|
|
switch (ptr_len) {
|
|
case PtrLenSingle:
|
|
return 0;
|
|
case PtrLenUnknown:
|
|
return 1;
|
|
case PtrLenC:
|
|
return 3;
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_type_entry) {
|
|
Error err;
|
|
ZigType *attrs_type;
|
|
uint32_t size_enum_index;
|
|
if (is_slice(ptr_type_entry)) {
|
|
attrs_type = ptr_type_entry->data.structure.fields[slice_ptr_index].type_entry;
|
|
size_enum_index = 2;
|
|
} else if (ptr_type_entry->id == ZigTypeIdPointer) {
|
|
attrs_type = ptr_type_entry;
|
|
size_enum_index = ptr_len_to_size_enum_index(ptr_type_entry->data.pointer.ptr_len);
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
|
|
if ((err = type_resolve(ira->codegen, attrs_type->data.pointer.child_type, ResolveStatusAlignmentKnown)))
|
|
return nullptr;
|
|
|
|
ZigType *type_info_pointer_type = ir_type_info_get_type(ira, "Pointer", nullptr);
|
|
assertNoError(ensure_complete_type(ira->codegen, type_info_pointer_type));
|
|
|
|
ConstExprValue *result = create_const_vals(1);
|
|
result->special = ConstValSpecialStatic;
|
|
result->type = type_info_pointer_type;
|
|
|
|
ConstExprValue *fields = create_const_vals(6);
|
|
result->data.x_struct.fields = fields;
|
|
|
|
// size: Size
|
|
ensure_field_index(result->type, "size", 0);
|
|
ZigType *type_info_pointer_size_type = ir_type_info_get_type(ira, "Size", type_info_pointer_type);
|
|
assertNoError(ensure_complete_type(ira->codegen, type_info_pointer_size_type));
|
|
fields[0].special = ConstValSpecialStatic;
|
|
fields[0].type = type_info_pointer_size_type;
|
|
bigint_init_unsigned(&fields[0].data.x_enum_tag, size_enum_index);
|
|
|
|
// is_const: bool
|
|
ensure_field_index(result->type, "is_const", 1);
|
|
fields[1].special = ConstValSpecialStatic;
|
|
fields[1].type = ira->codegen->builtin_types.entry_bool;
|
|
fields[1].data.x_bool = attrs_type->data.pointer.is_const;
|
|
// is_volatile: bool
|
|
ensure_field_index(result->type, "is_volatile", 2);
|
|
fields[2].special = ConstValSpecialStatic;
|
|
fields[2].type = ira->codegen->builtin_types.entry_bool;
|
|
fields[2].data.x_bool = attrs_type->data.pointer.is_volatile;
|
|
// alignment: u32
|
|
ensure_field_index(result->type, "alignment", 3);
|
|
fields[3].special = ConstValSpecialStatic;
|
|
fields[3].type = ira->codegen->builtin_types.entry_num_lit_int;
|
|
bigint_init_unsigned(&fields[3].data.x_bigint, get_ptr_align(ira->codegen, attrs_type));
|
|
// child: type
|
|
ensure_field_index(result->type, "child", 4);
|
|
fields[4].special = ConstValSpecialStatic;
|
|
fields[4].type = ira->codegen->builtin_types.entry_type;
|
|
fields[4].data.x_type = attrs_type->data.pointer.child_type;
|
|
// is_allowzero: bool
|
|
ensure_field_index(result->type, "is_allowzero", 5);
|
|
fields[5].special = ConstValSpecialStatic;
|
|
fields[5].type = ira->codegen->builtin_types.entry_bool;
|
|
fields[5].data.x_bool = attrs_type->data.pointer.allow_zero;
|
|
|
|
return result;
|
|
};
|
|
|
|
static void make_enum_field_val(IrAnalyze *ira, ConstExprValue *enum_field_val, TypeEnumField *enum_field,
|
|
ZigType *type_info_enum_field_type)
|
|
{
|
|
enum_field_val->special = ConstValSpecialStatic;
|
|
enum_field_val->type = type_info_enum_field_type;
|
|
|
|
ConstExprValue *inner_fields = create_const_vals(2);
|
|
inner_fields[1].special = ConstValSpecialStatic;
|
|
inner_fields[1].type = ira->codegen->builtin_types.entry_num_lit_int;
|
|
|
|
ConstExprValue *name = create_const_str_lit(ira->codegen, enum_field->name);
|
|
init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(enum_field->name), true);
|
|
|
|
bigint_init_bigint(&inner_fields[1].data.x_bigint, &enum_field->value);
|
|
|
|
enum_field_val->data.x_struct.fields = inner_fields;
|
|
}
|
|
|
|
static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr, ZigType *type_entry,
|
|
ConstExprValue **out)
|
|
{
|
|
Error err;
|
|
assert(type_entry != nullptr);
|
|
assert(!type_is_invalid(type_entry));
|
|
|
|
if ((err = type_resolve(ira->codegen, type_entry, ResolveStatusSizeKnown)))
|
|
return err;
|
|
|
|
auto entry = ira->codegen->type_info_cache.maybe_get(type_entry);
|
|
if (entry != nullptr) {
|
|
*out = entry->value;
|
|
return ErrorNone;
|
|
}
|
|
|
|
ConstExprValue *result = nullptr;
|
|
switch (type_entry->id) {
|
|
case ZigTypeIdInvalid:
|
|
zig_unreachable();
|
|
case ZigTypeIdMetaType:
|
|
case ZigTypeIdVoid:
|
|
case ZigTypeIdBool:
|
|
case ZigTypeIdUnreachable:
|
|
case ZigTypeIdComptimeFloat:
|
|
case ZigTypeIdComptimeInt:
|
|
case ZigTypeIdEnumLiteral:
|
|
case ZigTypeIdUndefined:
|
|
case ZigTypeIdNull:
|
|
case ZigTypeIdArgTuple:
|
|
case ZigTypeIdOpaque:
|
|
result = &ira->codegen->const_void_val;
|
|
break;
|
|
case ZigTypeIdInt:
|
|
{
|
|
result = create_const_vals(1);
|
|
result->special = ConstValSpecialStatic;
|
|
result->type = ir_type_info_get_type(ira, "Int", nullptr);
|
|
|
|
ConstExprValue *fields = create_const_vals(2);
|
|
result->data.x_struct.fields = fields;
|
|
|
|
// is_signed: bool
|
|
ensure_field_index(result->type, "is_signed", 0);
|
|
fields[0].special = ConstValSpecialStatic;
|
|
fields[0].type = ira->codegen->builtin_types.entry_bool;
|
|
fields[0].data.x_bool = type_entry->data.integral.is_signed;
|
|
// bits: u8
|
|
ensure_field_index(result->type, "bits", 1);
|
|
fields[1].special = ConstValSpecialStatic;
|
|
fields[1].type = ira->codegen->builtin_types.entry_num_lit_int;
|
|
bigint_init_unsigned(&fields[1].data.x_bigint, type_entry->data.integral.bit_count);
|
|
|
|
break;
|
|
}
|
|
case ZigTypeIdFloat:
|
|
{
|
|
result = create_const_vals(1);
|
|
result->special = ConstValSpecialStatic;
|
|
result->type = ir_type_info_get_type(ira, "Float", nullptr);
|
|
|
|
ConstExprValue *fields = create_const_vals(1);
|
|
result->data.x_struct.fields = fields;
|
|
|
|
// bits: u8
|
|
ensure_field_index(result->type, "bits", 0);
|
|
fields[0].special = ConstValSpecialStatic;
|
|
fields[0].type = ira->codegen->builtin_types.entry_num_lit_int;
|
|
bigint_init_unsigned(&fields->data.x_bigint, type_entry->data.floating.bit_count);
|
|
|
|
break;
|
|
}
|
|
case ZigTypeIdPointer:
|
|
{
|
|
result = create_ptr_like_type_info(ira, type_entry);
|
|
if (result == nullptr)
|
|
return ErrorSemanticAnalyzeFail;
|
|
break;
|
|
}
|
|
case ZigTypeIdArray:
|
|
{
|
|
result = create_const_vals(1);
|
|
result->special = ConstValSpecialStatic;
|
|
result->type = ir_type_info_get_type(ira, "Array", nullptr);
|
|
|
|
ConstExprValue *fields = create_const_vals(2);
|
|
result->data.x_struct.fields = fields;
|
|
|
|
// len: usize
|
|
ensure_field_index(result->type, "len", 0);
|
|
fields[0].special = ConstValSpecialStatic;
|
|
fields[0].type = ira->codegen->builtin_types.entry_num_lit_int;
|
|
bigint_init_unsigned(&fields[0].data.x_bigint, type_entry->data.array.len);
|
|
// child: type
|
|
ensure_field_index(result->type, "child", 1);
|
|
fields[1].special = ConstValSpecialStatic;
|
|
fields[1].type = ira->codegen->builtin_types.entry_type;
|
|
fields[1].data.x_type = type_entry->data.array.child_type;
|
|
|
|
break;
|
|
}
|
|
case ZigTypeIdVector: {
|
|
result = create_const_vals(1);
|
|
result->special = ConstValSpecialStatic;
|
|
result->type = ir_type_info_get_type(ira, "Vector", nullptr);
|
|
|
|
ConstExprValue *fields = create_const_vals(2);
|
|
result->data.x_struct.fields = fields;
|
|
|
|
// len: usize
|
|
ensure_field_index(result->type, "len", 0);
|
|
fields[0].special = ConstValSpecialStatic;
|
|
fields[0].type = ira->codegen->builtin_types.entry_num_lit_int;
|
|
bigint_init_unsigned(&fields[0].data.x_bigint, type_entry->data.vector.len);
|
|
// child: type
|
|
ensure_field_index(result->type, "child", 1);
|
|
fields[1].special = ConstValSpecialStatic;
|
|
fields[1].type = ira->codegen->builtin_types.entry_type;
|
|
fields[1].data.x_type = type_entry->data.vector.elem_type;
|
|
|
|
break;
|
|
}
|
|
case ZigTypeIdOptional:
|
|
{
|
|
result = create_const_vals(1);
|
|
result->special = ConstValSpecialStatic;
|
|
result->type = ir_type_info_get_type(ira, "Optional", nullptr);
|
|
|
|
ConstExprValue *fields = create_const_vals(1);
|
|
result->data.x_struct.fields = fields;
|
|
|
|
// child: type
|
|
ensure_field_index(result->type, "child", 0);
|
|
fields[0].special = ConstValSpecialStatic;
|
|
fields[0].type = ira->codegen->builtin_types.entry_type;
|
|
fields[0].data.x_type = type_entry->data.maybe.child_type;
|
|
|
|
break;
|
|
}
|
|
case ZigTypeIdAnyFrame: {
|
|
result = create_const_vals(1);
|
|
result->special = ConstValSpecialStatic;
|
|
result->type = ir_type_info_get_type(ira, "AnyFrame", nullptr);
|
|
|
|
ConstExprValue *fields = create_const_vals(1);
|
|
result->data.x_struct.fields = fields;
|
|
|
|
// child: ?type
|
|
ensure_field_index(result->type, "child", 0);
|
|
fields[0].special = ConstValSpecialStatic;
|
|
fields[0].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type);
|
|
fields[0].data.x_optional = (type_entry->data.any_frame.result_type == nullptr) ? nullptr :
|
|
create_const_type(ira->codegen, type_entry->data.any_frame.result_type);
|
|
break;
|
|
}
|
|
case ZigTypeIdEnum:
|
|
{
|
|
result = create_const_vals(1);
|
|
result->special = ConstValSpecialStatic;
|
|
result->type = ir_type_info_get_type(ira, "Enum", nullptr);
|
|
|
|
ConstExprValue *fields = create_const_vals(4);
|
|
result->data.x_struct.fields = fields;
|
|
|
|
// layout: ContainerLayout
|
|
ensure_field_index(result->type, "layout", 0);
|
|
fields[0].special = ConstValSpecialStatic;
|
|
fields[0].type = ir_type_info_get_type(ira, "ContainerLayout", nullptr);
|
|
bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.enumeration.layout);
|
|
// tag_type: type
|
|
ensure_field_index(result->type, "tag_type", 1);
|
|
fields[1].special = ConstValSpecialStatic;
|
|
fields[1].type = ira->codegen->builtin_types.entry_type;
|
|
fields[1].data.x_type = type_entry->data.enumeration.tag_int_type;
|
|
// fields: []TypeInfo.EnumField
|
|
ensure_field_index(result->type, "fields", 2);
|
|
|
|
ZigType *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField", nullptr);
|
|
if ((err = type_resolve(ira->codegen, type_info_enum_field_type, ResolveStatusSizeKnown))) {
|
|
zig_unreachable();
|
|
}
|
|
uint32_t enum_field_count = type_entry->data.enumeration.src_field_count;
|
|
|
|
ConstExprValue *enum_field_array = create_const_vals(1);
|
|
enum_field_array->special = ConstValSpecialStatic;
|
|
enum_field_array->type = get_array_type(ira->codegen, type_info_enum_field_type, enum_field_count);
|
|
enum_field_array->data.x_array.special = ConstArraySpecialNone;
|
|
enum_field_array->data.x_array.data.s_none.elements = create_const_vals(enum_field_count);
|
|
|
|
init_const_slice(ira->codegen, &fields[2], enum_field_array, 0, enum_field_count, false);
|
|
|
|
for (uint32_t enum_field_index = 0; enum_field_index < enum_field_count; enum_field_index++)
|
|
{
|
|
TypeEnumField *enum_field = &type_entry->data.enumeration.fields[enum_field_index];
|
|
ConstExprValue *enum_field_val = &enum_field_array->data.x_array.data.s_none.elements[enum_field_index];
|
|
make_enum_field_val(ira, enum_field_val, enum_field, type_info_enum_field_type);
|
|
enum_field_val->parent.id = ConstParentIdArray;
|
|
enum_field_val->parent.data.p_array.array_val = enum_field_array;
|
|
enum_field_val->parent.data.p_array.elem_index = enum_field_index;
|
|
}
|
|
// decls: []TypeInfo.Declaration
|
|
ensure_field_index(result->type, "decls", 3);
|
|
if ((err = ir_make_type_info_decls(ira, source_instr, &fields[3],
|
|
type_entry->data.enumeration.decls_scope)))
|
|
{
|
|
return err;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case ZigTypeIdErrorSet:
|
|
{
|
|
result = create_const_vals(1);
|
|
result->special = ConstValSpecialStatic;
|
|
result->type = ir_type_info_get_type(ira, "ErrorSet", nullptr);
|
|
|
|
ZigType *type_info_error_type = ir_type_info_get_type(ira, "Error", nullptr);
|
|
if (!resolve_inferred_error_set(ira->codegen, type_entry, source_instr->source_node)) {
|
|
return ErrorSemanticAnalyzeFail;
|
|
}
|
|
if (type_is_global_error_set(type_entry)) {
|
|
result->data.x_optional = nullptr;
|
|
break;
|
|
}
|
|
if ((err = type_resolve(ira->codegen, type_info_error_type, ResolveStatusSizeKnown))) {
|
|
zig_unreachable();
|
|
}
|
|
ConstExprValue *slice_val = create_const_vals(1);
|
|
result->data.x_optional = slice_val;
|
|
|
|
uint32_t error_count = type_entry->data.error_set.err_count;
|
|
ConstExprValue *error_array = create_const_vals(1);
|
|
error_array->special = ConstValSpecialStatic;
|
|
error_array->type = get_array_type(ira->codegen, type_info_error_type, error_count);
|
|
error_array->data.x_array.special = ConstArraySpecialNone;
|
|
error_array->data.x_array.data.s_none.elements = create_const_vals(error_count);
|
|
|
|
init_const_slice(ira->codegen, slice_val, error_array, 0, error_count, false);
|
|
for (uint32_t error_index = 0; error_index < error_count; error_index++) {
|
|
ErrorTableEntry *error = type_entry->data.error_set.errors[error_index];
|
|
ConstExprValue *error_val = &error_array->data.x_array.data.s_none.elements[error_index];
|
|
|
|
error_val->special = ConstValSpecialStatic;
|
|
error_val->type = type_info_error_type;
|
|
|
|
ConstExprValue *inner_fields = create_const_vals(2);
|
|
inner_fields[1].special = ConstValSpecialStatic;
|
|
inner_fields[1].type = ira->codegen->builtin_types.entry_num_lit_int;
|
|
|
|
ConstExprValue *name = nullptr;
|
|
if (error->cached_error_name_val != nullptr)
|
|
name = error->cached_error_name_val;
|
|
if (name == nullptr)
|
|
name = create_const_str_lit(ira->codegen, &error->name);
|
|
init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(&error->name), true);
|
|
bigint_init_unsigned(&inner_fields[1].data.x_bigint, error->value);
|
|
|
|
error_val->data.x_struct.fields = inner_fields;
|
|
error_val->parent.id = ConstParentIdArray;
|
|
error_val->parent.data.p_array.array_val = error_array;
|
|
error_val->parent.data.p_array.elem_index = error_index;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case ZigTypeIdErrorUnion:
|
|
{
|
|
result = create_const_vals(1);
|
|
result->special = ConstValSpecialStatic;
|
|
result->type = ir_type_info_get_type(ira, "ErrorUnion", nullptr);
|
|
|
|
ConstExprValue *fields = create_const_vals(2);
|
|
result->data.x_struct.fields = fields;
|
|
|
|
// error_set: type
|
|
ensure_field_index(result->type, "error_set", 0);
|
|
fields[0].special = ConstValSpecialStatic;
|
|
fields[0].type = ira->codegen->builtin_types.entry_type;
|
|
fields[0].data.x_type = type_entry->data.error_union.err_set_type;
|
|
|
|
// payload: type
|
|
ensure_field_index(result->type, "payload", 1);
|
|
fields[1].special = ConstValSpecialStatic;
|
|
fields[1].type = ira->codegen->builtin_types.entry_type;
|
|
fields[1].data.x_type = type_entry->data.error_union.payload_type;
|
|
|
|
break;
|
|
}
|
|
case ZigTypeIdUnion:
|
|
{
|
|
result = create_const_vals(1);
|
|
result->special = ConstValSpecialStatic;
|
|
result->type = ir_type_info_get_type(ira, "Union", nullptr);
|
|
|
|
ConstExprValue *fields = create_const_vals(4);
|
|
result->data.x_struct.fields = fields;
|
|
|
|
// layout: ContainerLayout
|
|
ensure_field_index(result->type, "layout", 0);
|
|
fields[0].special = ConstValSpecialStatic;
|
|
fields[0].type = ir_type_info_get_type(ira, "ContainerLayout", nullptr);
|
|
bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.unionation.layout);
|
|
// tag_type: ?type
|
|
ensure_field_index(result->type, "tag_type", 1);
|
|
fields[1].special = ConstValSpecialStatic;
|
|
fields[1].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type);
|
|
|
|
AstNode *union_decl_node = type_entry->data.unionation.decl_node;
|
|
if (union_decl_node->data.container_decl.auto_enum ||
|
|
union_decl_node->data.container_decl.init_arg_expr != nullptr)
|
|
{
|
|
ConstExprValue *tag_type = create_const_vals(1);
|
|
tag_type->special = ConstValSpecialStatic;
|
|
tag_type->type = ira->codegen->builtin_types.entry_type;
|
|
tag_type->data.x_type = type_entry->data.unionation.tag_type;
|
|
fields[1].data.x_optional = tag_type;
|
|
} else {
|
|
fields[1].data.x_optional = nullptr;
|
|
}
|
|
// fields: []TypeInfo.UnionField
|
|
ensure_field_index(result->type, "fields", 2);
|
|
|
|
ZigType *type_info_union_field_type = ir_type_info_get_type(ira, "UnionField", nullptr);
|
|
if ((err = type_resolve(ira->codegen, type_info_union_field_type, ResolveStatusSizeKnown)))
|
|
zig_unreachable();
|
|
uint32_t union_field_count = type_entry->data.unionation.src_field_count;
|
|
|
|
ConstExprValue *union_field_array = create_const_vals(1);
|
|
union_field_array->special = ConstValSpecialStatic;
|
|
union_field_array->type = get_array_type(ira->codegen, type_info_union_field_type, union_field_count);
|
|
union_field_array->data.x_array.special = ConstArraySpecialNone;
|
|
union_field_array->data.x_array.data.s_none.elements = create_const_vals(union_field_count);
|
|
|
|
init_const_slice(ira->codegen, &fields[2], union_field_array, 0, union_field_count, false);
|
|
|
|
ZigType *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField", nullptr);
|
|
|
|
for (uint32_t union_field_index = 0; union_field_index < union_field_count; union_field_index++) {
|
|
TypeUnionField *union_field = &type_entry->data.unionation.fields[union_field_index];
|
|
ConstExprValue *union_field_val = &union_field_array->data.x_array.data.s_none.elements[union_field_index];
|
|
|
|
union_field_val->special = ConstValSpecialStatic;
|
|
union_field_val->type = type_info_union_field_type;
|
|
|
|
ConstExprValue *inner_fields = create_const_vals(3);
|
|
inner_fields[1].special = ConstValSpecialStatic;
|
|
inner_fields[1].type = get_optional_type(ira->codegen, type_info_enum_field_type);
|
|
|
|
if (fields[1].data.x_optional == nullptr) {
|
|
inner_fields[1].data.x_optional = nullptr;
|
|
} else {
|
|
inner_fields[1].data.x_optional = create_const_vals(1);
|
|
make_enum_field_val(ira, inner_fields[1].data.x_optional, union_field->enum_field, type_info_enum_field_type);
|
|
}
|
|
|
|
inner_fields[2].special = ConstValSpecialStatic;
|
|
inner_fields[2].type = ira->codegen->builtin_types.entry_type;
|
|
inner_fields[2].data.x_type = union_field->type_entry;
|
|
|
|
ConstExprValue *name = create_const_str_lit(ira->codegen, union_field->name);
|
|
init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(union_field->name), true);
|
|
|
|
union_field_val->data.x_struct.fields = inner_fields;
|
|
union_field_val->parent.id = ConstParentIdArray;
|
|
union_field_val->parent.data.p_array.array_val = union_field_array;
|
|
union_field_val->parent.data.p_array.elem_index = union_field_index;
|
|
}
|
|
// decls: []TypeInfo.Declaration
|
|
ensure_field_index(result->type, "decls", 3);
|
|
if ((err = ir_make_type_info_decls(ira, source_instr, &fields[3],
|
|
type_entry->data.unionation.decls_scope)))
|
|
{
|
|
return err;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case ZigTypeIdStruct:
|
|
{
|
|
if (type_entry->data.structure.is_slice) {
|
|
result = create_ptr_like_type_info(ira, type_entry);
|
|
if (result == nullptr)
|
|
return ErrorSemanticAnalyzeFail;
|
|
break;
|
|
}
|
|
|
|
result = create_const_vals(1);
|
|
result->special = ConstValSpecialStatic;
|
|
result->type = ir_type_info_get_type(ira, "Struct", nullptr);
|
|
|
|
ConstExprValue *fields = create_const_vals(3);
|
|
result->data.x_struct.fields = fields;
|
|
|
|
// layout: ContainerLayout
|
|
ensure_field_index(result->type, "layout", 0);
|
|
fields[0].special = ConstValSpecialStatic;
|
|
fields[0].type = ir_type_info_get_type(ira, "ContainerLayout", nullptr);
|
|
bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.structure.layout);
|
|
// fields: []TypeInfo.StructField
|
|
ensure_field_index(result->type, "fields", 1);
|
|
|
|
ZigType *type_info_struct_field_type = ir_type_info_get_type(ira, "StructField", nullptr);
|
|
if ((err = type_resolve(ira->codegen, type_info_struct_field_type, ResolveStatusSizeKnown))) {
|
|
zig_unreachable();
|
|
}
|
|
uint32_t struct_field_count = type_entry->data.structure.src_field_count;
|
|
|
|
ConstExprValue *struct_field_array = create_const_vals(1);
|
|
struct_field_array->special = ConstValSpecialStatic;
|
|
struct_field_array->type = get_array_type(ira->codegen, type_info_struct_field_type, struct_field_count);
|
|
struct_field_array->data.x_array.special = ConstArraySpecialNone;
|
|
struct_field_array->data.x_array.data.s_none.elements = create_const_vals(struct_field_count);
|
|
|
|
init_const_slice(ira->codegen, &fields[1], struct_field_array, 0, struct_field_count, false);
|
|
|
|
for (uint32_t struct_field_index = 0; struct_field_index < struct_field_count; struct_field_index++) {
|
|
TypeStructField *struct_field = &type_entry->data.structure.fields[struct_field_index];
|
|
ConstExprValue *struct_field_val = &struct_field_array->data.x_array.data.s_none.elements[struct_field_index];
|
|
|
|
struct_field_val->special = ConstValSpecialStatic;
|
|
struct_field_val->type = type_info_struct_field_type;
|
|
|
|
ConstExprValue *inner_fields = create_const_vals(3);
|
|
inner_fields[1].special = ConstValSpecialStatic;
|
|
inner_fields[1].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_num_lit_int);
|
|
|
|
if (!type_has_bits(struct_field->type_entry)) {
|
|
inner_fields[1].data.x_optional = nullptr;
|
|
} else {
|
|
size_t byte_offset = struct_field->offset;
|
|
inner_fields[1].data.x_optional = create_const_vals(1);
|
|
inner_fields[1].data.x_optional->special = ConstValSpecialStatic;
|
|
inner_fields[1].data.x_optional->type = ira->codegen->builtin_types.entry_num_lit_int;
|
|
bigint_init_unsigned(&inner_fields[1].data.x_optional->data.x_bigint, byte_offset);
|
|
}
|
|
|
|
inner_fields[2].special = ConstValSpecialStatic;
|
|
inner_fields[2].type = ira->codegen->builtin_types.entry_type;
|
|
inner_fields[2].data.x_type = struct_field->type_entry;
|
|
|
|
ConstExprValue *name = create_const_str_lit(ira->codegen, struct_field->name);
|
|
init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(struct_field->name), true);
|
|
|
|
struct_field_val->data.x_struct.fields = inner_fields;
|
|
struct_field_val->parent.id = ConstParentIdArray;
|
|
struct_field_val->parent.data.p_array.array_val = struct_field_array;
|
|
struct_field_val->parent.data.p_array.elem_index = struct_field_index;
|
|
}
|
|
// decls: []TypeInfo.Declaration
|
|
ensure_field_index(result->type, "decls", 2);
|
|
if ((err = ir_make_type_info_decls(ira, source_instr, &fields[2],
|
|
type_entry->data.structure.decls_scope)))
|
|
{
|
|
return err;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case ZigTypeIdFn:
|
|
{
|
|
result = create_const_vals(1);
|
|
result->special = ConstValSpecialStatic;
|
|
result->type = ir_type_info_get_type(ira, "Fn", nullptr);
|
|
|
|
ConstExprValue *fields = create_const_vals(5);
|
|
result->data.x_struct.fields = fields;
|
|
|
|
// calling_convention: TypeInfo.CallingConvention
|
|
ensure_field_index(result->type, "calling_convention", 0);
|
|
fields[0].special = ConstValSpecialStatic;
|
|
fields[0].type = ir_type_info_get_type(ira, "CallingConvention", nullptr);
|
|
bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.fn.fn_type_id.cc);
|
|
// is_generic: bool
|
|
ensure_field_index(result->type, "is_generic", 1);
|
|
bool is_generic = type_entry->data.fn.is_generic;
|
|
fields[1].special = ConstValSpecialStatic;
|
|
fields[1].type = ira->codegen->builtin_types.entry_bool;
|
|
fields[1].data.x_bool = is_generic;
|
|
// is_varargs: bool
|
|
ensure_field_index(result->type, "is_var_args", 2);
|
|
bool is_varargs = type_entry->data.fn.fn_type_id.is_var_args;
|
|
fields[2].special = ConstValSpecialStatic;
|
|
fields[2].type = ira->codegen->builtin_types.entry_bool;
|
|
fields[2].data.x_bool = type_entry->data.fn.fn_type_id.is_var_args;
|
|
// return_type: ?type
|
|
ensure_field_index(result->type, "return_type", 3);
|
|
fields[3].special = ConstValSpecialStatic;
|
|
fields[3].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type);
|
|
if (type_entry->data.fn.fn_type_id.return_type == nullptr)
|
|
fields[3].data.x_optional = nullptr;
|
|
else {
|
|
ConstExprValue *return_type = create_const_vals(1);
|
|
return_type->special = ConstValSpecialStatic;
|
|
return_type->type = ira->codegen->builtin_types.entry_type;
|
|
return_type->data.x_type = type_entry->data.fn.fn_type_id.return_type;
|
|
fields[3].data.x_optional = return_type;
|
|
}
|
|
// args: []TypeInfo.FnArg
|
|
ZigType *type_info_fn_arg_type = ir_type_info_get_type(ira, "FnArg", nullptr);
|
|
if ((err = type_resolve(ira->codegen, type_info_fn_arg_type, ResolveStatusSizeKnown))) {
|
|
zig_unreachable();
|
|
}
|
|
size_t fn_arg_count = type_entry->data.fn.fn_type_id.param_count -
|
|
(is_varargs && type_entry->data.fn.fn_type_id.cc != CallingConventionC);
|
|
|
|
ConstExprValue *fn_arg_array = create_const_vals(1);
|
|
fn_arg_array->special = ConstValSpecialStatic;
|
|
fn_arg_array->type = get_array_type(ira->codegen, type_info_fn_arg_type, fn_arg_count);
|
|
fn_arg_array->data.x_array.special = ConstArraySpecialNone;
|
|
fn_arg_array->data.x_array.data.s_none.elements = create_const_vals(fn_arg_count);
|
|
|
|
init_const_slice(ira->codegen, &fields[4], fn_arg_array, 0, fn_arg_count, false);
|
|
|
|
for (size_t fn_arg_index = 0; fn_arg_index < fn_arg_count; fn_arg_index++) {
|
|
FnTypeParamInfo *fn_param_info = &type_entry->data.fn.fn_type_id.param_info[fn_arg_index];
|
|
ConstExprValue *fn_arg_val = &fn_arg_array->data.x_array.data.s_none.elements[fn_arg_index];
|
|
|
|
fn_arg_val->special = ConstValSpecialStatic;
|
|
fn_arg_val->type = type_info_fn_arg_type;
|
|
|
|
bool arg_is_generic = fn_param_info->type == nullptr;
|
|
if (arg_is_generic) assert(is_generic);
|
|
|
|
ConstExprValue *inner_fields = create_const_vals(3);
|
|
inner_fields[0].special = ConstValSpecialStatic;
|
|
inner_fields[0].type = ira->codegen->builtin_types.entry_bool;
|
|
inner_fields[0].data.x_bool = arg_is_generic;
|
|
inner_fields[1].special = ConstValSpecialStatic;
|
|
inner_fields[1].type = ira->codegen->builtin_types.entry_bool;
|
|
inner_fields[1].data.x_bool = fn_param_info->is_noalias;
|
|
inner_fields[2].special = ConstValSpecialStatic;
|
|
inner_fields[2].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type);
|
|
|
|
if (arg_is_generic)
|
|
inner_fields[2].data.x_optional = nullptr;
|
|
else {
|
|
ConstExprValue *arg_type = create_const_vals(1);
|
|
arg_type->special = ConstValSpecialStatic;
|
|
arg_type->type = ira->codegen->builtin_types.entry_type;
|
|
arg_type->data.x_type = fn_param_info->type;
|
|
inner_fields[2].data.x_optional = arg_type;
|
|
}
|
|
|
|
fn_arg_val->data.x_struct.fields = inner_fields;
|
|
fn_arg_val->parent.id = ConstParentIdArray;
|
|
fn_arg_val->parent.data.p_array.array_val = fn_arg_array;
|
|
fn_arg_val->parent.data.p_array.elem_index = fn_arg_index;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case ZigTypeIdBoundFn:
|
|
{
|
|
ZigType *fn_type = type_entry->data.bound_fn.fn_type;
|
|
assert(fn_type->id == ZigTypeIdFn);
|
|
if ((err = ir_make_type_info_value(ira, source_instr, fn_type, &result)))
|
|
return err;
|
|
|
|
break;
|
|
}
|
|
case ZigTypeIdCoroFrame:
|
|
zig_panic("TODO @typeInfo for async function frames");
|
|
}
|
|
|
|
assert(result != nullptr);
|
|
ira->codegen->type_info_cache.put(type_entry, result);
|
|
*out = result;
|
|
return ErrorNone;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_type_info(IrAnalyze *ira,
|
|
IrInstructionTypeInfo *instruction)
|
|
{
|
|
Error err;
|
|
IrInstruction *type_value = instruction->type_value->child;
|
|
ZigType *type_entry = ir_resolve_type(ira, type_value);
|
|
if (type_is_invalid(type_entry))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *result_type = ir_type_info_get_type(ira, nullptr, nullptr);
|
|
|
|
ConstExprValue *payload;
|
|
if ((err = ir_make_type_info_value(ira, &instruction->base, type_entry, &payload)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *result = ir_const(ira, &instruction->base, result_type);
|
|
ConstExprValue *out_val = &result->value;
|
|
bigint_init_unsigned(&out_val->data.x_union.tag, type_id_index(type_entry));
|
|
out_val->data.x_union.payload = payload;
|
|
|
|
if (payload != nullptr) {
|
|
payload->parent.id = ConstParentIdUnion;
|
|
payload->parent.data.p_union.union_val = out_val;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_type_id(IrAnalyze *ira,
|
|
IrInstructionTypeId *instruction)
|
|
{
|
|
IrInstruction *type_value = instruction->type_value->child;
|
|
ZigType *type_entry = ir_resolve_type(ira, type_value);
|
|
if (type_is_invalid(type_entry))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ConstExprValue *var_value = get_builtin_value(ira->codegen, "TypeId");
|
|
assert(var_value->type->id == ZigTypeIdMetaType);
|
|
ZigType *result_type = var_value->data.x_type;
|
|
|
|
IrInstruction *result = ir_const(ira, &instruction->base, result_type);
|
|
bigint_init_unsigned(&result->value.data.x_enum_tag, type_id_index(type_entry));
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_set_eval_branch_quota(IrAnalyze *ira,
|
|
IrInstructionSetEvalBranchQuota *instruction)
|
|
{
|
|
uint64_t new_quota;
|
|
if (!ir_resolve_usize(ira, instruction->new_quota->child, &new_quota))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (new_quota > *ira->new_irb.exec->backward_branch_quota) {
|
|
*ira->new_irb.exec->backward_branch_quota = new_quota;
|
|
}
|
|
|
|
return ir_const_void(ira, &instruction->base);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_type_name(IrAnalyze *ira, IrInstructionTypeName *instruction) {
|
|
IrInstruction *type_value = instruction->type_value->child;
|
|
ZigType *type_entry = ir_resolve_type(ira, type_value);
|
|
if (type_is_invalid(type_entry))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (!type_entry->cached_const_name_val) {
|
|
type_entry->cached_const_name_val = create_const_str_lit(ira->codegen, type_bare_name(type_entry));
|
|
}
|
|
IrInstruction *result = ir_const(ira, &instruction->base, nullptr);
|
|
copy_const_val(&result->value, type_entry->cached_const_name_val, true);
|
|
return result;
|
|
}
|
|
|
|
static void ir_cimport_cache_paths(Buf *cache_dir, Buf *tmp_c_file_digest, Buf *out_zig_dir, Buf *out_zig_path) {
|
|
buf_resize(out_zig_dir, 0);
|
|
buf_resize(out_zig_path, 0);
|
|
buf_appendf(out_zig_dir, "%s" OS_SEP "o" OS_SEP "%s",
|
|
buf_ptr(cache_dir), buf_ptr(tmp_c_file_digest));
|
|
buf_appendf(out_zig_path, "%s" OS_SEP "cimport.zig", buf_ptr(out_zig_dir));
|
|
}
|
|
static IrInstruction *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstructionCImport *instruction) {
|
|
Error err;
|
|
AstNode *node = instruction->base.source_node;
|
|
assert(node->type == NodeTypeFnCallExpr);
|
|
AstNode *block_node = node->data.fn_call_expr.params.at(0);
|
|
|
|
ScopeCImport *cimport_scope = create_cimport_scope(ira->codegen, node, instruction->base.scope);
|
|
|
|
// Execute the C import block like an inline function
|
|
ZigType *void_type = ira->codegen->builtin_types.entry_void;
|
|
ConstExprValue *cimport_result = ir_eval_const_value(ira->codegen, &cimport_scope->base, block_node, void_type,
|
|
ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, nullptr,
|
|
&cimport_scope->buf, block_node, nullptr, nullptr, nullptr);
|
|
if (type_is_invalid(cimport_result->type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigPackage *cur_scope_pkg = scope_package(instruction->base.scope);
|
|
Buf *namespace_name = buf_sprintf("%s.cimport:%" ZIG_PRI_usize ":%" ZIG_PRI_usize,
|
|
buf_ptr(&cur_scope_pkg->pkg_path), node->line + 1, node->column + 1);
|
|
|
|
ZigPackage *cimport_pkg = new_anonymous_package();
|
|
cimport_pkg->package_table.put(buf_create_from_str("builtin"), ira->codegen->compile_var_package);
|
|
cimport_pkg->package_table.put(buf_create_from_str("std"), ira->codegen->std_package);
|
|
buf_init_from_buf(&cimport_pkg->pkg_path, namespace_name);
|
|
|
|
CacheHash *cache_hash;
|
|
if ((err = create_c_object_cache(ira->codegen, &cache_hash, false))) {
|
|
ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to create cache: %s", err_str(err)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
cache_buf(cache_hash, &cimport_scope->buf);
|
|
|
|
// Set this because we're not adding any files before checking for a hit.
|
|
cache_hash->force_check_manifest = true;
|
|
|
|
Buf tmp_c_file_digest = BUF_INIT;
|
|
buf_resize(&tmp_c_file_digest, 0);
|
|
if ((err = cache_hit(cache_hash, &tmp_c_file_digest))) {
|
|
if (err != ErrorInvalidFormat) {
|
|
ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to check cache: %s", err_str(err)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
ira->codegen->caches_to_release.append(cache_hash);
|
|
|
|
Buf *out_zig_dir = buf_alloc();
|
|
Buf *out_zig_path = buf_alloc();
|
|
if (buf_len(&tmp_c_file_digest) == 0 || cache_hash->files.length == 0) {
|
|
// Cache Miss
|
|
Buf *tmp_c_file_dir = buf_sprintf("%s" OS_SEP "o" OS_SEP "%s",
|
|
buf_ptr(ira->codegen->cache_dir), buf_ptr(&cache_hash->b64_digest));
|
|
Buf *resolve_paths[] = {
|
|
tmp_c_file_dir,
|
|
buf_create_from_str("cimport.h"),
|
|
};
|
|
Buf tmp_c_file_path = os_path_resolve(resolve_paths, 2);
|
|
|
|
if ((err = os_make_path(tmp_c_file_dir))) {
|
|
ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to make dir: %s", err_str(err)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if ((err = os_write_file(&tmp_c_file_path, &cimport_scope->buf))) {
|
|
ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to write .h file: %s", err_str(err)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
if (ira->codegen->verbose_cimport) {
|
|
fprintf(stderr, "@cImport source: %s\n", buf_ptr(&tmp_c_file_path));
|
|
}
|
|
|
|
Buf *tmp_dep_file = buf_sprintf("%s.d", buf_ptr(&tmp_c_file_path));
|
|
|
|
ZigList<const char *> clang_argv = {0};
|
|
|
|
add_cc_args(ira->codegen, clang_argv, buf_ptr(tmp_dep_file), true);
|
|
|
|
clang_argv.append(buf_ptr(&tmp_c_file_path));
|
|
|
|
if (ira->codegen->verbose_cc) {
|
|
fprintf(stderr, "clang");
|
|
for (size_t i = 0; i < clang_argv.length; i += 1) {
|
|
fprintf(stderr, " %s", clang_argv.at(i));
|
|
}
|
|
fprintf(stderr, "\n");
|
|
}
|
|
|
|
clang_argv.append(nullptr); // to make the [start...end] argument work
|
|
|
|
AstNode *root_node;
|
|
Stage2ErrorMsg *errors_ptr;
|
|
size_t errors_len;
|
|
|
|
const char *resources_path = buf_ptr(ira->codegen->zig_c_headers_dir);
|
|
|
|
if ((err = parse_h_file(ira->codegen, &root_node, &errors_ptr, &errors_len,
|
|
&clang_argv.at(0), &clang_argv.last(), Stage2TranslateModeImport, resources_path)))
|
|
{
|
|
if (err != ErrorCCompileErrors) {
|
|
ir_add_error_node(ira, node, buf_sprintf("C import failed: %s", err_str(err)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
ErrorMsg *parent_err_msg = ir_add_error_node(ira, node, buf_sprintf("C import failed"));
|
|
if (ira->codegen->libc_link_lib == nullptr) {
|
|
add_error_note(ira->codegen, parent_err_msg, node,
|
|
buf_sprintf("libc headers not available; compilation does not link against libc"));
|
|
}
|
|
for (size_t i = 0; i < errors_len; i += 1) {
|
|
Stage2ErrorMsg *clang_err = &errors_ptr[i];
|
|
ErrorMsg *err_msg = err_msg_create_with_offset(
|
|
clang_err->filename_ptr ?
|
|
buf_create_from_mem(clang_err->filename_ptr, clang_err->filename_len) : buf_alloc(),
|
|
clang_err->line, clang_err->column, clang_err->offset, clang_err->source,
|
|
buf_create_from_mem(clang_err->msg_ptr, clang_err->msg_len));
|
|
err_msg_add_note(parent_err_msg, err_msg);
|
|
}
|
|
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
if (ira->codegen->verbose_cimport) {
|
|
fprintf(stderr, "@cImport .d file: %s\n", buf_ptr(tmp_dep_file));
|
|
}
|
|
|
|
if ((err = cache_add_dep_file(cache_hash, tmp_dep_file, false))) {
|
|
ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to parse .d file: %s", err_str(err)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
if ((err = cache_final(cache_hash, &tmp_c_file_digest))) {
|
|
ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to finalize cache: %s", err_str(err)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
ir_cimport_cache_paths(ira->codegen->cache_dir, &tmp_c_file_digest, out_zig_dir, out_zig_path);
|
|
if ((err = os_make_path(out_zig_dir))) {
|
|
ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to make output dir: %s", err_str(err)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
FILE *out_file = fopen(buf_ptr(out_zig_path), "wb");
|
|
if (out_file == nullptr) {
|
|
ir_add_error_node(ira, node,
|
|
buf_sprintf("C import failed: unable to open output file: %s", strerror(errno)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
ast_render(out_file, root_node, 4);
|
|
if (fclose(out_file) != 0) {
|
|
ir_add_error_node(ira, node,
|
|
buf_sprintf("C import failed: unable to write to output file: %s", strerror(errno)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (ira->codegen->verbose_cimport) {
|
|
fprintf(stderr, "@cImport output: %s\n", buf_ptr(out_zig_path));
|
|
}
|
|
|
|
} else {
|
|
// Cache Hit
|
|
ir_cimport_cache_paths(ira->codegen->cache_dir, &tmp_c_file_digest, out_zig_dir, out_zig_path);
|
|
if (ira->codegen->verbose_cimport) {
|
|
fprintf(stderr, "@cImport cache hit: %s\n", buf_ptr(out_zig_path));
|
|
}
|
|
}
|
|
|
|
Buf *import_code = buf_alloc();
|
|
if ((err = file_fetch(ira->codegen, out_zig_path, import_code))) {
|
|
ir_add_error_node(ira, node,
|
|
buf_sprintf("unable to open '%s': %s", buf_ptr(out_zig_path), err_str(err)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
ZigType *child_import = add_source_file(ira->codegen, cimport_pkg, out_zig_path,
|
|
import_code, SourceKindCImport);
|
|
return ir_const_type(ira, &instruction->base, child_import);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_c_include(IrAnalyze *ira, IrInstructionCInclude *instruction) {
|
|
IrInstruction *name_value = instruction->name->child;
|
|
if (type_is_invalid(name_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
Buf *include_name = ir_resolve_str(ira, name_value);
|
|
if (!include_name)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
Buf *c_import_buf = exec_c_import_buf(ira->new_irb.exec);
|
|
// We check for this error in pass1
|
|
assert(c_import_buf);
|
|
|
|
buf_appendf(c_import_buf, "#include <%s>\n", buf_ptr(include_name));
|
|
|
|
return ir_const_void(ira, &instruction->base);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_c_define(IrAnalyze *ira, IrInstructionCDefine *instruction) {
|
|
IrInstruction *name = instruction->name->child;
|
|
if (type_is_invalid(name->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
Buf *define_name = ir_resolve_str(ira, name);
|
|
if (!define_name)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *value = instruction->value->child;
|
|
if (type_is_invalid(value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
Buf *define_value = ir_resolve_str(ira, value);
|
|
if (!define_value)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
Buf *c_import_buf = exec_c_import_buf(ira->new_irb.exec);
|
|
// We check for this error in pass1
|
|
assert(c_import_buf);
|
|
|
|
buf_appendf(c_import_buf, "#define %s %s\n", buf_ptr(define_name), buf_ptr(define_value));
|
|
|
|
return ir_const_void(ira, &instruction->base);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_c_undef(IrAnalyze *ira, IrInstructionCUndef *instruction) {
|
|
IrInstruction *name = instruction->name->child;
|
|
if (type_is_invalid(name->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
Buf *undef_name = ir_resolve_str(ira, name);
|
|
if (!undef_name)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
Buf *c_import_buf = exec_c_import_buf(ira->new_irb.exec);
|
|
// We check for this error in pass1
|
|
assert(c_import_buf);
|
|
|
|
buf_appendf(c_import_buf, "#undef %s\n", buf_ptr(undef_name));
|
|
|
|
return ir_const_void(ira, &instruction->base);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstructionEmbedFile *instruction) {
|
|
IrInstruction *name = instruction->name->child;
|
|
if (type_is_invalid(name->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
Buf *rel_file_path = ir_resolve_str(ira, name);
|
|
if (!rel_file_path)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *import = get_scope_import(instruction->base.scope);
|
|
// figure out absolute path to resource
|
|
Buf source_dir_path = BUF_INIT;
|
|
os_path_dirname(import->data.structure.root_struct->path, &source_dir_path);
|
|
|
|
Buf *resolve_paths[] = {
|
|
&source_dir_path,
|
|
rel_file_path,
|
|
};
|
|
Buf *file_path = buf_alloc();
|
|
*file_path = os_path_resolve(resolve_paths, 2);
|
|
|
|
// load from file system into const expr
|
|
Buf *file_contents = buf_alloc();
|
|
Error err;
|
|
if ((err = file_fetch(ira->codegen, file_path, file_contents))) {
|
|
if (err == ErrorFileNotFound) {
|
|
ir_add_error(ira, instruction->name, buf_sprintf("unable to find '%s'", buf_ptr(file_path)));
|
|
return ira->codegen->invalid_instruction;
|
|
} else {
|
|
ir_add_error(ira, instruction->name, buf_sprintf("unable to open '%s': %s", buf_ptr(file_path), err_str(err)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
|
|
ZigType *result_type = get_array_type(ira->codegen,
|
|
ira->codegen->builtin_types.entry_u8, buf_len(file_contents));
|
|
IrInstruction *result = ir_const(ira, &instruction->base, result_type);
|
|
init_const_str_lit(ira->codegen, &result->value, file_contents);
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructionCmpxchgSrc *instruction) {
|
|
ZigType *operand_type = ir_resolve_atomic_operand_type(ira, instruction->type_value->child);
|
|
if (type_is_invalid(operand_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *ptr = instruction->ptr->child;
|
|
if (type_is_invalid(ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
// TODO let this be volatile
|
|
ZigType *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false);
|
|
IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr, ptr_type);
|
|
if (type_is_invalid(casted_ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *cmp_value = instruction->cmp_value->child;
|
|
if (type_is_invalid(cmp_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *new_value = instruction->new_value->child;
|
|
if (type_is_invalid(new_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *success_order_value = instruction->success_order_value->child;
|
|
if (type_is_invalid(success_order_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
AtomicOrder success_order;
|
|
if (!ir_resolve_atomic_order(ira, success_order_value, &success_order))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *failure_order_value = instruction->failure_order_value->child;
|
|
if (type_is_invalid(failure_order_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
AtomicOrder failure_order;
|
|
if (!ir_resolve_atomic_order(ira, failure_order_value, &failure_order))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *casted_cmp_value = ir_implicit_cast(ira, cmp_value, operand_type);
|
|
if (type_is_invalid(casted_cmp_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *casted_new_value = ir_implicit_cast(ira, new_value, operand_type);
|
|
if (type_is_invalid(casted_new_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (success_order < AtomicOrderMonotonic) {
|
|
ir_add_error(ira, success_order_value,
|
|
buf_sprintf("success atomic ordering must be Monotonic or stricter"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
if (failure_order < AtomicOrderMonotonic) {
|
|
ir_add_error(ira, failure_order_value,
|
|
buf_sprintf("failure atomic ordering must be Monotonic or stricter"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
if (failure_order > success_order) {
|
|
ir_add_error(ira, failure_order_value,
|
|
buf_sprintf("failure atomic ordering must be no stricter than success"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
if (failure_order == AtomicOrderRelease || failure_order == AtomicOrderAcqRel) {
|
|
ir_add_error(ira, failure_order_value,
|
|
buf_sprintf("failure atomic ordering must not be Release or AcqRel"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (instr_is_comptime(casted_ptr) && instr_is_comptime(casted_cmp_value) && instr_is_comptime(casted_new_value)) {
|
|
zig_panic("TODO compile-time execution of cmpxchg");
|
|
}
|
|
|
|
ZigType *result_type = get_optional_type(ira->codegen, operand_type);
|
|
IrInstruction *result_loc;
|
|
if (handle_is_ptr(result_type)) {
|
|
result_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc,
|
|
result_type, nullptr, true, false, true);
|
|
if (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc)) {
|
|
return result_loc;
|
|
}
|
|
} else {
|
|
result_loc = nullptr;
|
|
}
|
|
|
|
return ir_build_cmpxchg_gen(ira, &instruction->base, result_type,
|
|
casted_ptr, casted_cmp_value, casted_new_value,
|
|
success_order, failure_order, instruction->is_weak, result_loc);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstructionFence *instruction) {
|
|
IrInstruction *order_value = instruction->order_value->child;
|
|
if (type_is_invalid(order_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
AtomicOrder order;
|
|
if (!ir_resolve_atomic_order(ira, order_value, &order))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *result = ir_build_fence(&ira->new_irb,
|
|
instruction->base.scope, instruction->base.source_node, order_value, order);
|
|
result->value.type = ira->codegen->builtin_types.entry_void;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstructionTruncate *instruction) {
|
|
IrInstruction *dest_type_value = instruction->dest_type->child;
|
|
ZigType *dest_type = ir_resolve_type(ira, dest_type_value);
|
|
if (type_is_invalid(dest_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (dest_type->id != ZigTypeIdInt &&
|
|
dest_type->id != ZigTypeIdComptimeInt)
|
|
{
|
|
ir_add_error(ira, dest_type_value, buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *target = instruction->target->child;
|
|
ZigType *src_type = target->value.type;
|
|
if (type_is_invalid(src_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (src_type->id != ZigTypeIdInt &&
|
|
src_type->id != ZigTypeIdComptimeInt)
|
|
{
|
|
ir_add_error(ira, target, buf_sprintf("expected integer type, found '%s'", buf_ptr(&src_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (dest_type->id == ZigTypeIdComptimeInt) {
|
|
return ir_implicit_cast(ira, target, dest_type);
|
|
}
|
|
|
|
if (instr_is_comptime(target)) {
|
|
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
|
|
if (val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *result = ir_const(ira, &instruction->base, dest_type);
|
|
bigint_truncate(&result->value.data.x_bigint, &val->data.x_bigint,
|
|
dest_type->data.integral.bit_count, dest_type->data.integral.is_signed);
|
|
return result;
|
|
}
|
|
|
|
if (src_type->data.integral.bit_count == 0 || dest_type->data.integral.bit_count == 0) {
|
|
IrInstruction *result = ir_const(ira, &instruction->base, dest_type);
|
|
bigint_init_unsigned(&result->value.data.x_bigint, 0);
|
|
return result;
|
|
}
|
|
|
|
if (src_type->data.integral.is_signed != dest_type->data.integral.is_signed) {
|
|
const char *sign_str = dest_type->data.integral.is_signed ? "signed" : "unsigned";
|
|
ir_add_error(ira, target, buf_sprintf("expected %s integer type, found '%s'", sign_str, buf_ptr(&src_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
} else if (src_type->data.integral.bit_count < dest_type->data.integral.bit_count) {
|
|
ir_add_error(ira, target, buf_sprintf("type '%s' has fewer bits than destination type '%s'",
|
|
buf_ptr(&src_type->name), buf_ptr(&dest_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *new_instruction = ir_build_truncate(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node, dest_type_value, target);
|
|
new_instruction->value.type = dest_type;
|
|
return new_instruction;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_int_cast(IrAnalyze *ira, IrInstructionIntCast *instruction) {
|
|
ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child);
|
|
if (type_is_invalid(dest_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (dest_type->id != ZigTypeIdInt && dest_type->id != ZigTypeIdComptimeInt) {
|
|
ir_add_error(ira, instruction->dest_type, buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *target = instruction->target->child;
|
|
if (type_is_invalid(target->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (target->value.type->id != ZigTypeIdInt && target->value.type->id != ZigTypeIdComptimeInt) {
|
|
ir_add_error(ira, instruction->target, buf_sprintf("expected integer type, found '%s'",
|
|
buf_ptr(&target->value.type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (instr_is_comptime(target)) {
|
|
return ir_implicit_cast(ira, target, dest_type);
|
|
}
|
|
|
|
if (dest_type->id == ZigTypeIdComptimeInt) {
|
|
ir_add_error(ira, instruction->target, buf_sprintf("attempt to cast runtime value to '%s'",
|
|
buf_ptr(&dest_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
return ir_analyze_widen_or_shorten(ira, &instruction->base, target, dest_type);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_float_cast(IrAnalyze *ira, IrInstructionFloatCast *instruction) {
|
|
ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child);
|
|
if (type_is_invalid(dest_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (dest_type->id != ZigTypeIdFloat) {
|
|
ir_add_error(ira, instruction->dest_type,
|
|
buf_sprintf("expected float type, found '%s'", buf_ptr(&dest_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *target = instruction->target->child;
|
|
if (type_is_invalid(target->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (target->value.type->id == ZigTypeIdComptimeInt ||
|
|
target->value.type->id == ZigTypeIdComptimeFloat)
|
|
{
|
|
if (ir_num_lit_fits_in_other_type(ira, target, dest_type, true)) {
|
|
CastOp op;
|
|
if (target->value.type->id == ZigTypeIdComptimeInt) {
|
|
op = CastOpIntToFloat;
|
|
} else {
|
|
op = CastOpNumLitToConcrete;
|
|
}
|
|
return ir_resolve_cast(ira, &instruction->base, target, dest_type, op);
|
|
} else {
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
|
|
if (target->value.type->id != ZigTypeIdFloat) {
|
|
ir_add_error(ira, instruction->target, buf_sprintf("expected float type, found '%s'",
|
|
buf_ptr(&target->value.type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
return ir_analyze_widen_or_shorten(ira, &instruction->base, target, dest_type);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_err_set_cast(IrAnalyze *ira, IrInstructionErrSetCast *instruction) {
|
|
ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child);
|
|
if (type_is_invalid(dest_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (dest_type->id != ZigTypeIdErrorSet) {
|
|
ir_add_error(ira, instruction->dest_type,
|
|
buf_sprintf("expected error set type, found '%s'", buf_ptr(&dest_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *target = instruction->target->child;
|
|
if (type_is_invalid(target->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (target->value.type->id != ZigTypeIdErrorSet) {
|
|
ir_add_error(ira, instruction->target,
|
|
buf_sprintf("expected error set type, found '%s'", buf_ptr(&target->value.type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
return ir_analyze_err_set_cast(ira, &instruction->base, target, dest_type);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_from_bytes(IrAnalyze *ira, IrInstructionFromBytes *instruction) {
|
|
Error err;
|
|
|
|
ZigType *dest_child_type = ir_resolve_type(ira, instruction->dest_child_type->child);
|
|
if (type_is_invalid(dest_child_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *target = instruction->target->child;
|
|
if (type_is_invalid(target->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
bool src_ptr_const;
|
|
bool src_ptr_volatile;
|
|
uint32_t src_ptr_align;
|
|
if (target->value.type->id == ZigTypeIdPointer) {
|
|
src_ptr_const = target->value.type->data.pointer.is_const;
|
|
src_ptr_volatile = target->value.type->data.pointer.is_volatile;
|
|
|
|
if ((err = resolve_ptr_align(ira, target->value.type, &src_ptr_align)))
|
|
return ira->codegen->invalid_instruction;
|
|
} else if (is_slice(target->value.type)) {
|
|
ZigType *src_ptr_type = target->value.type->data.structure.fields[slice_ptr_index].type_entry;
|
|
src_ptr_const = src_ptr_type->data.pointer.is_const;
|
|
src_ptr_volatile = src_ptr_type->data.pointer.is_volatile;
|
|
|
|
if ((err = resolve_ptr_align(ira, src_ptr_type, &src_ptr_align)))
|
|
return ira->codegen->invalid_instruction;
|
|
} else {
|
|
src_ptr_const = true;
|
|
src_ptr_volatile = false;
|
|
|
|
if ((err = type_resolve(ira->codegen, target->value.type, ResolveStatusAlignmentKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
src_ptr_align = get_abi_alignment(ira->codegen, target->value.type);
|
|
}
|
|
|
|
if ((err = type_resolve(ira->codegen, dest_child_type, ResolveStatusSizeKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *dest_ptr_type = get_pointer_to_type_extra(ira->codegen, dest_child_type,
|
|
src_ptr_const, src_ptr_volatile, PtrLenUnknown,
|
|
src_ptr_align, 0, 0, false);
|
|
ZigType *dest_slice_type = get_slice_type(ira->codegen, dest_ptr_type);
|
|
|
|
ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,
|
|
src_ptr_const, src_ptr_volatile, PtrLenUnknown,
|
|
src_ptr_align, 0, 0, false);
|
|
ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr);
|
|
|
|
IrInstruction *casted_value = ir_implicit_cast(ira, target, u8_slice);
|
|
if (type_is_invalid(casted_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
bool have_known_len = false;
|
|
uint64_t known_len;
|
|
|
|
if (instr_is_comptime(casted_value)) {
|
|
ConstExprValue *val = ir_resolve_const(ira, casted_value, UndefBad);
|
|
if (!val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ConstExprValue *len_val = &val->data.x_struct.fields[slice_len_index];
|
|
if (value_is_comptime(len_val)) {
|
|
known_len = bigint_as_unsigned(&len_val->data.x_bigint);
|
|
have_known_len = true;
|
|
}
|
|
}
|
|
|
|
IrInstruction *result_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc,
|
|
dest_slice_type, nullptr, true, false, true);
|
|
if (result_loc != nullptr && (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc))) {
|
|
return result_loc;
|
|
}
|
|
|
|
if (casted_value->value.data.rh_slice.id == RuntimeHintSliceIdLen) {
|
|
known_len = casted_value->value.data.rh_slice.len;
|
|
have_known_len = true;
|
|
}
|
|
|
|
if (have_known_len) {
|
|
uint64_t child_type_size = type_size(ira->codegen, dest_child_type);
|
|
uint64_t remainder = known_len % child_type_size;
|
|
if (remainder != 0) {
|
|
ErrorMsg *msg = ir_add_error(ira, &instruction->base,
|
|
buf_sprintf("unable to convert [%" ZIG_PRI_u64 "]u8 to %s: size mismatch",
|
|
known_len, buf_ptr(&dest_slice_type->name)));
|
|
add_error_note(ira->codegen, msg, instruction->dest_child_type->source_node,
|
|
buf_sprintf("%s has size %" ZIG_PRI_u64 "; remaining bytes: %" ZIG_PRI_u64,
|
|
buf_ptr(&dest_child_type->name), child_type_size, remainder));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
|
|
return ir_build_resize_slice(ira, &instruction->base, casted_value, dest_slice_type, result_loc);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_to_bytes(IrAnalyze *ira, IrInstructionToBytes *instruction) {
|
|
Error err;
|
|
|
|
IrInstruction *target = instruction->target->child;
|
|
if (type_is_invalid(target->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (!is_slice(target->value.type)) {
|
|
ir_add_error(ira, instruction->target,
|
|
buf_sprintf("expected slice, found '%s'", buf_ptr(&target->value.type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
ZigType *src_ptr_type = target->value.type->data.structure.fields[slice_ptr_index].type_entry;
|
|
|
|
uint32_t alignment;
|
|
if ((err = resolve_ptr_align(ira, src_ptr_type, &alignment)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *dest_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,
|
|
src_ptr_type->data.pointer.is_const, src_ptr_type->data.pointer.is_volatile, PtrLenUnknown,
|
|
alignment, 0, 0, false);
|
|
ZigType *dest_slice_type = get_slice_type(ira->codegen, dest_ptr_type);
|
|
|
|
if (instr_is_comptime(target)) {
|
|
ConstExprValue *target_val = ir_resolve_const(ira, target, UndefBad);
|
|
if (target_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *result = ir_const(ira, &instruction->base, dest_slice_type);
|
|
result->value.data.x_struct.fields = create_const_vals(2);
|
|
|
|
ConstExprValue *ptr_val = &result->value.data.x_struct.fields[slice_ptr_index];
|
|
ConstExprValue *target_ptr_val = &target_val->data.x_struct.fields[slice_ptr_index];
|
|
copy_const_val(ptr_val, target_ptr_val, false);
|
|
ptr_val->type = dest_ptr_type;
|
|
|
|
ConstExprValue *len_val = &result->value.data.x_struct.fields[slice_len_index];
|
|
len_val->special = ConstValSpecialStatic;
|
|
len_val->type = ira->codegen->builtin_types.entry_usize;
|
|
ConstExprValue *target_len_val = &target_val->data.x_struct.fields[slice_len_index];
|
|
ZigType *elem_type = src_ptr_type->data.pointer.child_type;
|
|
BigInt elem_size_bigint;
|
|
bigint_init_unsigned(&elem_size_bigint, type_size(ira->codegen, elem_type));
|
|
bigint_mul(&len_val->data.x_bigint, &target_len_val->data.x_bigint, &elem_size_bigint);
|
|
|
|
return result;
|
|
}
|
|
|
|
IrInstruction *result_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc,
|
|
dest_slice_type, nullptr, true, false, true);
|
|
if (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc)) {
|
|
return result_loc;
|
|
}
|
|
|
|
return ir_build_resize_slice(ira, &instruction->base, target, dest_slice_type, result_loc);
|
|
}
|
|
|
|
static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align) {
|
|
Error err;
|
|
|
|
ZigType *ptr_type = get_src_ptr_type(ty);
|
|
assert(ptr_type != nullptr);
|
|
if (ptr_type->id == ZigTypeIdPointer) {
|
|
if ((err = type_resolve(ira->codegen, ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown)))
|
|
return err;
|
|
}
|
|
|
|
*result_align = get_ptr_align(ira->codegen, ty);
|
|
return ErrorNone;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_int_to_float(IrAnalyze *ira, IrInstructionIntToFloat *instruction) {
|
|
ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child);
|
|
if (type_is_invalid(dest_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *target = instruction->target->child;
|
|
if (type_is_invalid(target->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (target->value.type->id != ZigTypeIdInt && target->value.type->id != ZigTypeIdComptimeInt) {
|
|
ir_add_error(ira, instruction->target, buf_sprintf("expected int type, found '%s'",
|
|
buf_ptr(&target->value.type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
return ir_resolve_cast(ira, &instruction->base, target, dest_type, CastOpIntToFloat);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_float_to_int(IrAnalyze *ira, IrInstructionFloatToInt *instruction) {
|
|
ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child);
|
|
if (type_is_invalid(dest_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *target = instruction->target->child;
|
|
if (type_is_invalid(target->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (target->value.type->id == ZigTypeIdComptimeInt) {
|
|
return ir_implicit_cast(ira, target, dest_type);
|
|
}
|
|
|
|
if (target->value.type->id != ZigTypeIdFloat && target->value.type->id != ZigTypeIdComptimeFloat) {
|
|
ir_add_error(ira, instruction->target, buf_sprintf("expected float type, found '%s'",
|
|
buf_ptr(&target->value.type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
return ir_resolve_cast(ira, &instruction->base, target, dest_type, CastOpFloatToInt);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_err_to_int(IrAnalyze *ira, IrInstructionErrToInt *instruction) {
|
|
IrInstruction *target = instruction->target->child;
|
|
if (type_is_invalid(target->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *casted_target;
|
|
if (target->value.type->id == ZigTypeIdErrorSet) {
|
|
casted_target = target;
|
|
} else {
|
|
casted_target = ir_implicit_cast(ira, target, ira->codegen->builtin_types.entry_global_error_set);
|
|
if (type_is_invalid(casted_target->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
return ir_analyze_err_to_int(ira, &instruction->base, casted_target, ira->codegen->err_tag_type);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_int_to_err(IrAnalyze *ira, IrInstructionIntToErr *instruction) {
|
|
IrInstruction *target = instruction->target->child;
|
|
if (type_is_invalid(target->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *casted_target = ir_implicit_cast(ira, target, ira->codegen->err_tag_type);
|
|
if (type_is_invalid(casted_target->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
return ir_analyze_int_to_err(ira, &instruction->base, casted_target, ira->codegen->builtin_types.entry_global_error_set);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_bool_to_int(IrAnalyze *ira, IrInstructionBoolToInt *instruction) {
|
|
IrInstruction *target = instruction->target->child;
|
|
if (type_is_invalid(target->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (target->value.type->id != ZigTypeIdBool) {
|
|
ir_add_error(ira, instruction->target, buf_sprintf("expected bool, found '%s'",
|
|
buf_ptr(&target->value.type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (instr_is_comptime(target)) {
|
|
bool is_true;
|
|
if (!ir_resolve_bool(ira, target, &is_true))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
return ir_const_unsigned(ira, &instruction->base, is_true ? 1 : 0);
|
|
}
|
|
|
|
ZigType *u1_type = get_int_type(ira->codegen, false, 1);
|
|
return ir_resolve_cast(ira, &instruction->base, target, u1_type, CastOpBoolToInt);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_int_type(IrAnalyze *ira, IrInstructionIntType *instruction) {
|
|
IrInstruction *is_signed_value = instruction->is_signed->child;
|
|
bool is_signed;
|
|
if (!ir_resolve_bool(ira, is_signed_value, &is_signed))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *bit_count_value = instruction->bit_count->child;
|
|
uint64_t bit_count;
|
|
if (!ir_resolve_unsigned(ira, bit_count_value, ira->codegen->builtin_types.entry_u16, &bit_count))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
return ir_const_type(ira, &instruction->base, get_int_type(ira->codegen, is_signed, (uint32_t)bit_count));
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_vector_type(IrAnalyze *ira, IrInstructionVectorType *instruction) {
|
|
uint64_t len;
|
|
if (!ir_resolve_unsigned(ira, instruction->len->child, ira->codegen->builtin_types.entry_u32, &len))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *elem_type = ir_resolve_type(ira, instruction->elem_type->child);
|
|
if (type_is_invalid(elem_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (!is_valid_vector_elem_type(elem_type)) {
|
|
ir_add_error(ira, instruction->elem_type,
|
|
buf_sprintf("vector element type must be integer, float, or pointer; '%s' is invalid",
|
|
buf_ptr(&elem_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
ZigType *vector_type = get_vector_type(ira->codegen, len, elem_type);
|
|
|
|
return ir_const_type(ira, &instruction->base, vector_type);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_bool_not(IrAnalyze *ira, IrInstructionBoolNot *instruction) {
|
|
IrInstruction *value = instruction->value->child;
|
|
if (type_is_invalid(value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *bool_type = ira->codegen->builtin_types.entry_bool;
|
|
|
|
IrInstruction *casted_value = ir_implicit_cast(ira, value, bool_type);
|
|
if (type_is_invalid(casted_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (instr_is_comptime(casted_value)) {
|
|
ConstExprValue *value = ir_resolve_const(ira, casted_value, UndefBad);
|
|
if (value == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
return ir_const_bool(ira, &instruction->base, !value->data.x_bool);
|
|
}
|
|
|
|
IrInstruction *result = ir_build_bool_not(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node, casted_value);
|
|
result->value.type = bool_type;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructionMemset *instruction) {
|
|
Error err;
|
|
|
|
IrInstruction *dest_ptr = instruction->dest_ptr->child;
|
|
if (type_is_invalid(dest_ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *byte_value = instruction->byte->child;
|
|
if (type_is_invalid(byte_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *count_value = instruction->count->child;
|
|
if (type_is_invalid(count_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *dest_uncasted_type = dest_ptr->value.type;
|
|
bool dest_is_volatile = (dest_uncasted_type->id == ZigTypeIdPointer) &&
|
|
dest_uncasted_type->data.pointer.is_volatile;
|
|
|
|
ZigType *usize = ira->codegen->builtin_types.entry_usize;
|
|
ZigType *u8 = ira->codegen->builtin_types.entry_u8;
|
|
uint32_t dest_align;
|
|
if (dest_uncasted_type->id == ZigTypeIdPointer) {
|
|
if ((err = resolve_ptr_align(ira, dest_uncasted_type, &dest_align)))
|
|
return ira->codegen->invalid_instruction;
|
|
} else {
|
|
dest_align = get_abi_alignment(ira->codegen, u8);
|
|
}
|
|
ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, u8, false, dest_is_volatile,
|
|
PtrLenUnknown, dest_align, 0, 0, false);
|
|
|
|
IrInstruction *casted_dest_ptr = ir_implicit_cast(ira, dest_ptr, u8_ptr);
|
|
if (type_is_invalid(casted_dest_ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *casted_byte = ir_implicit_cast(ira, byte_value, u8);
|
|
if (type_is_invalid(casted_byte->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *casted_count = ir_implicit_cast(ira, count_value, usize);
|
|
if (type_is_invalid(casted_count->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
// TODO test this at comptime with u8 and non-u8 types
|
|
if (casted_dest_ptr->value.special == ConstValSpecialStatic &&
|
|
casted_byte->value.special == ConstValSpecialStatic &&
|
|
casted_count->value.special == ConstValSpecialStatic &&
|
|
casted_dest_ptr->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr &&
|
|
casted_dest_ptr->value.data.x_ptr.mut != ConstPtrMutRuntimeVar)
|
|
{
|
|
ConstExprValue *dest_ptr_val = &casted_dest_ptr->value;
|
|
|
|
ConstExprValue *dest_elements;
|
|
size_t start;
|
|
size_t bound_end;
|
|
switch (dest_ptr_val->data.x_ptr.special) {
|
|
case ConstPtrSpecialInvalid:
|
|
case ConstPtrSpecialDiscard:
|
|
zig_unreachable();
|
|
case ConstPtrSpecialRef:
|
|
dest_elements = dest_ptr_val->data.x_ptr.data.ref.pointee;
|
|
start = 0;
|
|
bound_end = 1;
|
|
break;
|
|
case ConstPtrSpecialBaseArray:
|
|
{
|
|
ConstExprValue *array_val = dest_ptr_val->data.x_ptr.data.base_array.array_val;
|
|
expand_undef_array(ira->codegen, array_val);
|
|
dest_elements = array_val->data.x_array.data.s_none.elements;
|
|
start = dest_ptr_val->data.x_ptr.data.base_array.elem_index;
|
|
bound_end = array_val->type->data.array.len;
|
|
break;
|
|
}
|
|
case ConstPtrSpecialBaseStruct:
|
|
zig_panic("TODO memset on const inner struct");
|
|
case ConstPtrSpecialBaseErrorUnionCode:
|
|
zig_panic("TODO memset on const inner error union code");
|
|
case ConstPtrSpecialBaseErrorUnionPayload:
|
|
zig_panic("TODO memset on const inner error union payload");
|
|
case ConstPtrSpecialBaseOptionalPayload:
|
|
zig_panic("TODO memset on const inner optional payload");
|
|
case ConstPtrSpecialHardCodedAddr:
|
|
zig_unreachable();
|
|
case ConstPtrSpecialFunction:
|
|
zig_panic("TODO memset on ptr cast from function");
|
|
case ConstPtrSpecialNull:
|
|
zig_panic("TODO memset on null ptr");
|
|
}
|
|
|
|
size_t count = bigint_as_unsigned(&casted_count->value.data.x_bigint);
|
|
size_t end = start + count;
|
|
if (end > bound_end) {
|
|
ir_add_error(ira, count_value, buf_sprintf("out of bounds pointer access"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
ConstExprValue *byte_val = &casted_byte->value;
|
|
for (size_t i = start; i < end; i += 1) {
|
|
copy_const_val(&dest_elements[i], byte_val, true);
|
|
}
|
|
|
|
return ir_const_void(ira, &instruction->base);
|
|
}
|
|
|
|
IrInstruction *result = ir_build_memset(&ira->new_irb, instruction->base.scope, instruction->base.source_node,
|
|
casted_dest_ptr, casted_byte, casted_count);
|
|
result->value.type = ira->codegen->builtin_types.entry_void;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstructionMemcpy *instruction) {
|
|
Error err;
|
|
|
|
IrInstruction *dest_ptr = instruction->dest_ptr->child;
|
|
if (type_is_invalid(dest_ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *src_ptr = instruction->src_ptr->child;
|
|
if (type_is_invalid(src_ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *count_value = instruction->count->child;
|
|
if (type_is_invalid(count_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *u8 = ira->codegen->builtin_types.entry_u8;
|
|
ZigType *dest_uncasted_type = dest_ptr->value.type;
|
|
ZigType *src_uncasted_type = src_ptr->value.type;
|
|
bool dest_is_volatile = (dest_uncasted_type->id == ZigTypeIdPointer) &&
|
|
dest_uncasted_type->data.pointer.is_volatile;
|
|
bool src_is_volatile = (src_uncasted_type->id == ZigTypeIdPointer) &&
|
|
src_uncasted_type->data.pointer.is_volatile;
|
|
|
|
uint32_t dest_align;
|
|
if (dest_uncasted_type->id == ZigTypeIdPointer) {
|
|
if ((err = resolve_ptr_align(ira, dest_uncasted_type, &dest_align)))
|
|
return ira->codegen->invalid_instruction;
|
|
} else {
|
|
dest_align = get_abi_alignment(ira->codegen, u8);
|
|
}
|
|
|
|
uint32_t src_align;
|
|
if (src_uncasted_type->id == ZigTypeIdPointer) {
|
|
if ((err = resolve_ptr_align(ira, src_uncasted_type, &src_align)))
|
|
return ira->codegen->invalid_instruction;
|
|
} else {
|
|
src_align = get_abi_alignment(ira->codegen, u8);
|
|
}
|
|
|
|
ZigType *usize = ira->codegen->builtin_types.entry_usize;
|
|
ZigType *u8_ptr_mut = get_pointer_to_type_extra(ira->codegen, u8, false, dest_is_volatile,
|
|
PtrLenUnknown, dest_align, 0, 0, false);
|
|
ZigType *u8_ptr_const = get_pointer_to_type_extra(ira->codegen, u8, true, src_is_volatile,
|
|
PtrLenUnknown, src_align, 0, 0, false);
|
|
|
|
IrInstruction *casted_dest_ptr = ir_implicit_cast(ira, dest_ptr, u8_ptr_mut);
|
|
if (type_is_invalid(casted_dest_ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *casted_src_ptr = ir_implicit_cast(ira, src_ptr, u8_ptr_const);
|
|
if (type_is_invalid(casted_src_ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *casted_count = ir_implicit_cast(ira, count_value, usize);
|
|
if (type_is_invalid(casted_count->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
// TODO test this at comptime with u8 and non-u8 types
|
|
// TODO test with dest ptr being a global runtime variable
|
|
if (casted_dest_ptr->value.special == ConstValSpecialStatic &&
|
|
casted_src_ptr->value.special == ConstValSpecialStatic &&
|
|
casted_count->value.special == ConstValSpecialStatic &&
|
|
casted_dest_ptr->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr)
|
|
{
|
|
size_t count = bigint_as_unsigned(&casted_count->value.data.x_bigint);
|
|
|
|
ConstExprValue *dest_ptr_val = &casted_dest_ptr->value;
|
|
ConstExprValue *dest_elements;
|
|
size_t dest_start;
|
|
size_t dest_end;
|
|
switch (dest_ptr_val->data.x_ptr.special) {
|
|
case ConstPtrSpecialInvalid:
|
|
case ConstPtrSpecialDiscard:
|
|
zig_unreachable();
|
|
case ConstPtrSpecialRef:
|
|
dest_elements = dest_ptr_val->data.x_ptr.data.ref.pointee;
|
|
dest_start = 0;
|
|
dest_end = 1;
|
|
break;
|
|
case ConstPtrSpecialBaseArray:
|
|
{
|
|
ConstExprValue *array_val = dest_ptr_val->data.x_ptr.data.base_array.array_val;
|
|
expand_undef_array(ira->codegen, array_val);
|
|
dest_elements = array_val->data.x_array.data.s_none.elements;
|
|
dest_start = dest_ptr_val->data.x_ptr.data.base_array.elem_index;
|
|
dest_end = array_val->type->data.array.len;
|
|
break;
|
|
}
|
|
case ConstPtrSpecialBaseStruct:
|
|
zig_panic("TODO memcpy on const inner struct");
|
|
case ConstPtrSpecialBaseErrorUnionCode:
|
|
zig_panic("TODO memcpy on const inner error union code");
|
|
case ConstPtrSpecialBaseErrorUnionPayload:
|
|
zig_panic("TODO memcpy on const inner error union payload");
|
|
case ConstPtrSpecialBaseOptionalPayload:
|
|
zig_panic("TODO memcpy on const inner optional payload");
|
|
case ConstPtrSpecialHardCodedAddr:
|
|
zig_unreachable();
|
|
case ConstPtrSpecialFunction:
|
|
zig_panic("TODO memcpy on ptr cast from function");
|
|
case ConstPtrSpecialNull:
|
|
zig_panic("TODO memcpy on null ptr");
|
|
}
|
|
|
|
if (dest_start + count > dest_end) {
|
|
ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds pointer access"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
ConstExprValue *src_ptr_val = &casted_src_ptr->value;
|
|
ConstExprValue *src_elements;
|
|
size_t src_start;
|
|
size_t src_end;
|
|
|
|
switch (src_ptr_val->data.x_ptr.special) {
|
|
case ConstPtrSpecialInvalid:
|
|
case ConstPtrSpecialDiscard:
|
|
zig_unreachable();
|
|
case ConstPtrSpecialRef:
|
|
src_elements = src_ptr_val->data.x_ptr.data.ref.pointee;
|
|
src_start = 0;
|
|
src_end = 1;
|
|
break;
|
|
case ConstPtrSpecialBaseArray:
|
|
{
|
|
ConstExprValue *array_val = src_ptr_val->data.x_ptr.data.base_array.array_val;
|
|
expand_undef_array(ira->codegen, array_val);
|
|
src_elements = array_val->data.x_array.data.s_none.elements;
|
|
src_start = src_ptr_val->data.x_ptr.data.base_array.elem_index;
|
|
src_end = array_val->type->data.array.len;
|
|
break;
|
|
}
|
|
case ConstPtrSpecialBaseStruct:
|
|
zig_panic("TODO memcpy on const inner struct");
|
|
case ConstPtrSpecialBaseErrorUnionCode:
|
|
zig_panic("TODO memcpy on const inner error union code");
|
|
case ConstPtrSpecialBaseErrorUnionPayload:
|
|
zig_panic("TODO memcpy on const inner error union payload");
|
|
case ConstPtrSpecialBaseOptionalPayload:
|
|
zig_panic("TODO memcpy on const inner optional payload");
|
|
case ConstPtrSpecialHardCodedAddr:
|
|
zig_unreachable();
|
|
case ConstPtrSpecialFunction:
|
|
zig_panic("TODO memcpy on ptr cast from function");
|
|
case ConstPtrSpecialNull:
|
|
zig_panic("TODO memcpy on null ptr");
|
|
}
|
|
|
|
if (src_start + count > src_end) {
|
|
ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds pointer access"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
// TODO check for noalias violations - this should be generalized to work for any function
|
|
|
|
for (size_t i = 0; i < count; i += 1) {
|
|
copy_const_val(&dest_elements[dest_start + i], &src_elements[src_start + i], true);
|
|
}
|
|
|
|
return ir_const_void(ira, &instruction->base);
|
|
}
|
|
|
|
IrInstruction *result = ir_build_memcpy(&ira->new_irb, instruction->base.scope, instruction->base.source_node,
|
|
casted_dest_ptr, casted_src_ptr, casted_count);
|
|
result->value.type = ira->codegen->builtin_types.entry_void;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructionSliceSrc *instruction) {
|
|
IrInstruction *ptr_ptr = instruction->ptr->child;
|
|
if (type_is_invalid(ptr_ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *ptr_ptr_type = ptr_ptr->value.type;
|
|
assert(ptr_ptr_type->id == ZigTypeIdPointer);
|
|
ZigType *array_type = ptr_ptr_type->data.pointer.child_type;
|
|
|
|
IrInstruction *start = instruction->start->child;
|
|
if (type_is_invalid(start->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *usize = ira->codegen->builtin_types.entry_usize;
|
|
IrInstruction *casted_start = ir_implicit_cast(ira, start, usize);
|
|
if (type_is_invalid(casted_start->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *end;
|
|
if (instruction->end) {
|
|
end = instruction->end->child;
|
|
if (type_is_invalid(end->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
end = ir_implicit_cast(ira, end, usize);
|
|
if (type_is_invalid(end->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
} else {
|
|
end = nullptr;
|
|
}
|
|
|
|
ZigType *return_type;
|
|
|
|
if (array_type->id == ZigTypeIdArray) {
|
|
bool is_comptime_const = ptr_ptr->value.special == ConstValSpecialStatic &&
|
|
ptr_ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst;
|
|
ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.array.child_type,
|
|
ptr_ptr_type->data.pointer.is_const || is_comptime_const,
|
|
ptr_ptr_type->data.pointer.is_volatile,
|
|
PtrLenUnknown,
|
|
ptr_ptr_type->data.pointer.explicit_alignment, 0, 0, false);
|
|
return_type = get_slice_type(ira->codegen, slice_ptr_type);
|
|
} else if (array_type->id == ZigTypeIdPointer) {
|
|
if (array_type->data.pointer.ptr_len == PtrLenSingle) {
|
|
ZigType *main_type = array_type->data.pointer.child_type;
|
|
if (main_type->id == ZigTypeIdArray) {
|
|
ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen,
|
|
main_type->data.pointer.child_type,
|
|
array_type->data.pointer.is_const, array_type->data.pointer.is_volatile,
|
|
PtrLenUnknown,
|
|
array_type->data.pointer.explicit_alignment, 0, 0, false);
|
|
return_type = get_slice_type(ira->codegen, slice_ptr_type);
|
|
} else {
|
|
ir_add_error(ira, &instruction->base, buf_sprintf("slice of single-item pointer"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
} else {
|
|
if (array_type->data.pointer.ptr_len == PtrLenC) {
|
|
array_type = adjust_ptr_len(ira->codegen, array_type, PtrLenUnknown);
|
|
}
|
|
return_type = get_slice_type(ira->codegen, array_type);
|
|
if (!end) {
|
|
ir_add_error(ira, &instruction->base, buf_sprintf("slice of pointer must include end value"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
} else if (is_slice(array_type)) {
|
|
ZigType *ptr_type = array_type->data.structure.fields[slice_ptr_index].type_entry;
|
|
return_type = get_slice_type(ira->codegen, ptr_type);
|
|
} else {
|
|
ir_add_error(ira, &instruction->base,
|
|
buf_sprintf("slice of non-array type '%s'", buf_ptr(&array_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (instr_is_comptime(ptr_ptr) &&
|
|
value_is_comptime(&casted_start->value) &&
|
|
(!end || value_is_comptime(&end->value)))
|
|
{
|
|
ConstExprValue *array_val;
|
|
ConstExprValue *parent_ptr;
|
|
size_t abs_offset;
|
|
size_t rel_end;
|
|
bool ptr_is_undef = false;
|
|
if (array_type->id == ZigTypeIdArray ||
|
|
(array_type->id == ZigTypeIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle))
|
|
{
|
|
if (array_type->id == ZigTypeIdPointer) {
|
|
ZigType *child_array_type = array_type->data.pointer.child_type;
|
|
assert(child_array_type->id == ZigTypeIdArray);
|
|
parent_ptr = const_ptr_pointee(ira, ira->codegen, &ptr_ptr->value, instruction->base.source_node);
|
|
if (parent_ptr == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
array_val = const_ptr_pointee(ira, ira->codegen, parent_ptr, instruction->base.source_node);
|
|
if (array_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
rel_end = child_array_type->data.array.len;
|
|
abs_offset = 0;
|
|
} else {
|
|
array_val = const_ptr_pointee(ira, ira->codegen, &ptr_ptr->value, instruction->base.source_node);
|
|
if (array_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
rel_end = array_type->data.array.len;
|
|
parent_ptr = nullptr;
|
|
abs_offset = 0;
|
|
}
|
|
} else if (array_type->id == ZigTypeIdPointer) {
|
|
assert(array_type->data.pointer.ptr_len == PtrLenUnknown);
|
|
parent_ptr = const_ptr_pointee(ira, ira->codegen, &ptr_ptr->value, instruction->base.source_node);
|
|
if (parent_ptr == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (parent_ptr->special == ConstValSpecialUndef) {
|
|
array_val = nullptr;
|
|
abs_offset = 0;
|
|
rel_end = SIZE_MAX;
|
|
ptr_is_undef = true;
|
|
} else switch (parent_ptr->data.x_ptr.special) {
|
|
case ConstPtrSpecialInvalid:
|
|
case ConstPtrSpecialDiscard:
|
|
zig_unreachable();
|
|
case ConstPtrSpecialRef:
|
|
if (parent_ptr->data.x_ptr.data.ref.pointee->type->id == ZigTypeIdArray) {
|
|
array_val = parent_ptr->data.x_ptr.data.ref.pointee;
|
|
abs_offset = 0;
|
|
rel_end = array_val->type->data.array.len;
|
|
} else {
|
|
array_val = nullptr;
|
|
abs_offset = SIZE_MAX;
|
|
rel_end = 1;
|
|
}
|
|
break;
|
|
case ConstPtrSpecialBaseArray:
|
|
array_val = parent_ptr->data.x_ptr.data.base_array.array_val;
|
|
abs_offset = parent_ptr->data.x_ptr.data.base_array.elem_index;
|
|
rel_end = array_val->type->data.array.len - abs_offset;
|
|
break;
|
|
case ConstPtrSpecialBaseStruct:
|
|
zig_panic("TODO slice const inner struct");
|
|
case ConstPtrSpecialBaseErrorUnionCode:
|
|
zig_panic("TODO slice const inner error union code");
|
|
case ConstPtrSpecialBaseErrorUnionPayload:
|
|
zig_panic("TODO slice const inner error union payload");
|
|
case ConstPtrSpecialBaseOptionalPayload:
|
|
zig_panic("TODO slice const inner optional payload");
|
|
case ConstPtrSpecialHardCodedAddr:
|
|
array_val = nullptr;
|
|
abs_offset = 0;
|
|
rel_end = SIZE_MAX;
|
|
break;
|
|
case ConstPtrSpecialFunction:
|
|
zig_panic("TODO slice of ptr cast from function");
|
|
case ConstPtrSpecialNull:
|
|
zig_panic("TODO slice of null ptr");
|
|
}
|
|
} else if (is_slice(array_type)) {
|
|
ConstExprValue *slice_ptr = const_ptr_pointee(ira, ira->codegen, &ptr_ptr->value, instruction->base.source_node);
|
|
if (slice_ptr == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
parent_ptr = &slice_ptr->data.x_struct.fields[slice_ptr_index];
|
|
if (parent_ptr->special == ConstValSpecialUndef) {
|
|
ir_add_error(ira, &instruction->base, buf_sprintf("slice of undefined"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
ConstExprValue *len_val = &slice_ptr->data.x_struct.fields[slice_len_index];
|
|
|
|
switch (parent_ptr->data.x_ptr.special) {
|
|
case ConstPtrSpecialInvalid:
|
|
case ConstPtrSpecialDiscard:
|
|
zig_unreachable();
|
|
case ConstPtrSpecialRef:
|
|
array_val = nullptr;
|
|
abs_offset = SIZE_MAX;
|
|
rel_end = 1;
|
|
break;
|
|
case ConstPtrSpecialBaseArray:
|
|
array_val = parent_ptr->data.x_ptr.data.base_array.array_val;
|
|
abs_offset = parent_ptr->data.x_ptr.data.base_array.elem_index;
|
|
rel_end = bigint_as_unsigned(&len_val->data.x_bigint);
|
|
break;
|
|
case ConstPtrSpecialBaseStruct:
|
|
zig_panic("TODO slice const inner struct");
|
|
case ConstPtrSpecialBaseErrorUnionCode:
|
|
zig_panic("TODO slice const inner error union code");
|
|
case ConstPtrSpecialBaseErrorUnionPayload:
|
|
zig_panic("TODO slice const inner error union payload");
|
|
case ConstPtrSpecialBaseOptionalPayload:
|
|
zig_panic("TODO slice const inner optional payload");
|
|
case ConstPtrSpecialHardCodedAddr:
|
|
array_val = nullptr;
|
|
abs_offset = 0;
|
|
rel_end = bigint_as_unsigned(&len_val->data.x_bigint);
|
|
break;
|
|
case ConstPtrSpecialFunction:
|
|
zig_panic("TODO slice of slice cast from function");
|
|
case ConstPtrSpecialNull:
|
|
zig_panic("TODO slice of null");
|
|
}
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
|
|
uint64_t start_scalar = bigint_as_unsigned(&casted_start->value.data.x_bigint);
|
|
if (!ptr_is_undef && start_scalar > rel_end) {
|
|
ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
uint64_t end_scalar;
|
|
if (end) {
|
|
end_scalar = bigint_as_unsigned(&end->value.data.x_bigint);
|
|
} else {
|
|
end_scalar = rel_end;
|
|
}
|
|
if (!ptr_is_undef) {
|
|
if (end_scalar > rel_end) {
|
|
ir_add_error(ira, &instruction->base, buf_sprintf("out of bounds slice"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
if (start_scalar > end_scalar) {
|
|
ir_add_error(ira, &instruction->base, buf_sprintf("slice start is greater than end"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
if (ptr_is_undef && start_scalar != end_scalar) {
|
|
ir_add_error(ira, &instruction->base, buf_sprintf("non-zero length slice of undefined pointer"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *result = ir_const(ira, &instruction->base, return_type);
|
|
ConstExprValue *out_val = &result->value;
|
|
out_val->data.x_struct.fields = create_const_vals(2);
|
|
|
|
ConstExprValue *ptr_val = &out_val->data.x_struct.fields[slice_ptr_index];
|
|
|
|
if (array_val) {
|
|
size_t index = abs_offset + start_scalar;
|
|
bool is_const = slice_is_const(return_type);
|
|
init_const_ptr_array(ira->codegen, ptr_val, array_val, index, is_const, PtrLenUnknown);
|
|
if (array_type->id == ZigTypeIdArray) {
|
|
ptr_val->data.x_ptr.mut = ptr_ptr->value.data.x_ptr.mut;
|
|
} else if (is_slice(array_type)) {
|
|
ptr_val->data.x_ptr.mut = parent_ptr->data.x_ptr.mut;
|
|
} else if (array_type->id == ZigTypeIdPointer) {
|
|
ptr_val->data.x_ptr.mut = parent_ptr->data.x_ptr.mut;
|
|
}
|
|
} else if (ptr_is_undef) {
|
|
ptr_val->type = get_pointer_to_type(ira->codegen, parent_ptr->type->data.pointer.child_type,
|
|
slice_is_const(return_type));
|
|
ptr_val->special = ConstValSpecialUndef;
|
|
} else switch (parent_ptr->data.x_ptr.special) {
|
|
case ConstPtrSpecialInvalid:
|
|
case ConstPtrSpecialDiscard:
|
|
zig_unreachable();
|
|
case ConstPtrSpecialRef:
|
|
init_const_ptr_ref(ira->codegen, ptr_val,
|
|
parent_ptr->data.x_ptr.data.ref.pointee, slice_is_const(return_type));
|
|
break;
|
|
case ConstPtrSpecialBaseArray:
|
|
zig_unreachable();
|
|
case ConstPtrSpecialBaseStruct:
|
|
zig_panic("TODO");
|
|
case ConstPtrSpecialBaseErrorUnionCode:
|
|
zig_panic("TODO");
|
|
case ConstPtrSpecialBaseErrorUnionPayload:
|
|
zig_panic("TODO");
|
|
case ConstPtrSpecialBaseOptionalPayload:
|
|
zig_panic("TODO");
|
|
case ConstPtrSpecialHardCodedAddr:
|
|
init_const_ptr_hard_coded_addr(ira->codegen, ptr_val,
|
|
parent_ptr->type->data.pointer.child_type,
|
|
parent_ptr->data.x_ptr.data.hard_coded_addr.addr + start_scalar,
|
|
slice_is_const(return_type));
|
|
break;
|
|
case ConstPtrSpecialFunction:
|
|
zig_panic("TODO");
|
|
case ConstPtrSpecialNull:
|
|
zig_panic("TODO");
|
|
}
|
|
|
|
ConstExprValue *len_val = &out_val->data.x_struct.fields[slice_len_index];
|
|
init_const_usize(ira->codegen, len_val, end_scalar - start_scalar);
|
|
|
|
return result;
|
|
}
|
|
|
|
IrInstruction *result_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc,
|
|
return_type, nullptr, true, false, true);
|
|
if (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc)) {
|
|
return result_loc;
|
|
}
|
|
return ir_build_slice_gen(ira, &instruction->base, return_type,
|
|
ptr_ptr, casted_start, end, instruction->safety_check_on, result_loc);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_member_count(IrAnalyze *ira, IrInstructionMemberCount *instruction) {
|
|
Error err;
|
|
IrInstruction *container = instruction->container->child;
|
|
if (type_is_invalid(container->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
ZigType *container_type = ir_resolve_type(ira, container);
|
|
|
|
if ((err = ensure_complete_type(ira->codegen, container_type)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
uint64_t result;
|
|
if (type_is_invalid(container_type)) {
|
|
return ira->codegen->invalid_instruction;
|
|
} else if (container_type->id == ZigTypeIdEnum) {
|
|
result = container_type->data.enumeration.src_field_count;
|
|
} else if (container_type->id == ZigTypeIdStruct) {
|
|
result = container_type->data.structure.src_field_count;
|
|
} else if (container_type->id == ZigTypeIdUnion) {
|
|
result = container_type->data.unionation.src_field_count;
|
|
} else if (container_type->id == ZigTypeIdErrorSet) {
|
|
if (!resolve_inferred_error_set(ira->codegen, container_type, instruction->base.source_node)) {
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
if (type_is_global_error_set(container_type)) {
|
|
ir_add_error(ira, &instruction->base, buf_sprintf("global error set member count not available at comptime"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
result = container_type->data.error_set.err_count;
|
|
} else {
|
|
ir_add_error(ira, &instruction->base, buf_sprintf("no value count available for type '%s'", buf_ptr(&container_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
return ir_const_unsigned(ira, &instruction->base, result);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_member_type(IrAnalyze *ira, IrInstructionMemberType *instruction) {
|
|
Error err;
|
|
IrInstruction *container_type_value = instruction->container_type->child;
|
|
ZigType *container_type = ir_resolve_type(ira, container_type_value);
|
|
if (type_is_invalid(container_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if ((err = ensure_complete_type(ira->codegen, container_type)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
|
|
uint64_t member_index;
|
|
IrInstruction *index_value = instruction->member_index->child;
|
|
if (!ir_resolve_usize(ira, index_value, &member_index))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (container_type->id == ZigTypeIdStruct) {
|
|
if (member_index >= container_type->data.structure.src_field_count) {
|
|
ir_add_error(ira, index_value,
|
|
buf_sprintf("member index %" ZIG_PRI_u64 " out of bounds; '%s' has %" PRIu32 " members",
|
|
member_index, buf_ptr(&container_type->name), container_type->data.structure.src_field_count));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
TypeStructField *field = &container_type->data.structure.fields[member_index];
|
|
|
|
return ir_const_type(ira, &instruction->base, field->type_entry);
|
|
} else if (container_type->id == ZigTypeIdUnion) {
|
|
if (member_index >= container_type->data.unionation.src_field_count) {
|
|
ir_add_error(ira, index_value,
|
|
buf_sprintf("member index %" ZIG_PRI_u64 " out of bounds; '%s' has %" PRIu32 " members",
|
|
member_index, buf_ptr(&container_type->name), container_type->data.unionation.src_field_count));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
TypeUnionField *field = &container_type->data.unionation.fields[member_index];
|
|
|
|
return ir_const_type(ira, &instruction->base, field->type_entry);
|
|
} else {
|
|
ir_add_error(ira, container_type_value,
|
|
buf_sprintf("type '%s' does not support @memberType", buf_ptr(&container_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_member_name(IrAnalyze *ira, IrInstructionMemberName *instruction) {
|
|
Error err;
|
|
IrInstruction *container_type_value = instruction->container_type->child;
|
|
ZigType *container_type = ir_resolve_type(ira, container_type_value);
|
|
if (type_is_invalid(container_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if ((err = ensure_complete_type(ira->codegen, container_type)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
uint64_t member_index;
|
|
IrInstruction *index_value = instruction->member_index->child;
|
|
if (!ir_resolve_usize(ira, index_value, &member_index))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (container_type->id == ZigTypeIdStruct) {
|
|
if (member_index >= container_type->data.structure.src_field_count) {
|
|
ir_add_error(ira, index_value,
|
|
buf_sprintf("member index %" ZIG_PRI_u64 " out of bounds; '%s' has %" PRIu32 " members",
|
|
member_index, buf_ptr(&container_type->name), container_type->data.structure.src_field_count));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
TypeStructField *field = &container_type->data.structure.fields[member_index];
|
|
|
|
IrInstruction *result = ir_const(ira, &instruction->base, nullptr);
|
|
init_const_str_lit(ira->codegen, &result->value, field->name);
|
|
return result;
|
|
} else if (container_type->id == ZigTypeIdEnum) {
|
|
if (member_index >= container_type->data.enumeration.src_field_count) {
|
|
ir_add_error(ira, index_value,
|
|
buf_sprintf("member index %" ZIG_PRI_u64 " out of bounds; '%s' has %" PRIu32 " members",
|
|
member_index, buf_ptr(&container_type->name), container_type->data.enumeration.src_field_count));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
TypeEnumField *field = &container_type->data.enumeration.fields[member_index];
|
|
|
|
IrInstruction *result = ir_const(ira, &instruction->base, nullptr);
|
|
init_const_str_lit(ira->codegen, &result->value, field->name);
|
|
return result;
|
|
} else if (container_type->id == ZigTypeIdUnion) {
|
|
if (member_index >= container_type->data.unionation.src_field_count) {
|
|
ir_add_error(ira, index_value,
|
|
buf_sprintf("member index %" ZIG_PRI_u64 " out of bounds; '%s' has %" PRIu32 " members",
|
|
member_index, buf_ptr(&container_type->name), container_type->data.unionation.src_field_count));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
TypeUnionField *field = &container_type->data.unionation.fields[member_index];
|
|
|
|
IrInstruction *result = ir_const(ira, &instruction->base, nullptr);
|
|
init_const_str_lit(ira->codegen, &result->value, field->name);
|
|
return result;
|
|
} else {
|
|
ir_add_error(ira, container_type_value,
|
|
buf_sprintf("type '%s' does not support @memberName", buf_ptr(&container_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_has_field(IrAnalyze *ira, IrInstructionHasField *instruction) {
|
|
Error err;
|
|
ZigType *container_type = ir_resolve_type(ira, instruction->container_type->child);
|
|
if (type_is_invalid(container_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if ((err = type_resolve(ira->codegen, container_type, ResolveStatusZeroBitsKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
Buf *field_name = ir_resolve_str(ira, instruction->field_name->child);
|
|
if (field_name == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
bool result;
|
|
if (container_type->id == ZigTypeIdStruct) {
|
|
result = find_struct_type_field(container_type, field_name) != nullptr;
|
|
} else if (container_type->id == ZigTypeIdEnum) {
|
|
result = find_enum_type_field(container_type, field_name) != nullptr;
|
|
} else if (container_type->id == ZigTypeIdUnion) {
|
|
result = find_union_type_field(container_type, field_name) != nullptr;
|
|
} else {
|
|
ir_add_error(ira, instruction->container_type,
|
|
buf_sprintf("type '%s' does not support @hasField", buf_ptr(&container_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
return ir_const_bool(ira, &instruction->base, result);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_breakpoint(IrAnalyze *ira, IrInstructionBreakpoint *instruction) {
|
|
IrInstruction *result = ir_build_breakpoint(&ira->new_irb,
|
|
instruction->base.scope, instruction->base.source_node);
|
|
result->value.type = ira->codegen->builtin_types.entry_void;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_return_address(IrAnalyze *ira, IrInstructionReturnAddress *instruction) {
|
|
IrInstruction *result = ir_build_return_address(&ira->new_irb,
|
|
instruction->base.scope, instruction->base.source_node);
|
|
result->value.type = ira->codegen->builtin_types.entry_usize;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_frame_address(IrAnalyze *ira, IrInstructionFrameAddress *instruction) {
|
|
IrInstruction *result = ir_build_frame_address(&ira->new_irb,
|
|
instruction->base.scope, instruction->base.source_node);
|
|
result->value.type = ira->codegen->builtin_types.entry_usize;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_frame_handle(IrAnalyze *ira, IrInstructionFrameHandle *instruction) {
|
|
ZigFn *fn = exec_fn_entry(ira->new_irb.exec);
|
|
ir_assert(fn != nullptr, &instruction->base);
|
|
|
|
ZigType *frame_type = get_coro_frame_type(ira->codegen, fn);
|
|
ZigType *ptr_frame_type = get_pointer_to_type(ira->codegen, frame_type, false);
|
|
|
|
IrInstruction *result = ir_build_handle(&ira->new_irb, instruction->base.scope, instruction->base.source_node);
|
|
result->value.type = ptr_frame_type;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_frame_type(IrAnalyze *ira, IrInstructionFrameType *instruction) {
|
|
ZigFn *fn = ir_resolve_fn(ira, instruction->fn->child);
|
|
if (fn == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *ty = get_coro_frame_type(ira->codegen, fn);
|
|
return ir_const_type(ira, &instruction->base, ty);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_frame_size(IrAnalyze *ira, IrInstructionFrameSizeSrc *instruction) {
|
|
IrInstruction *fn = instruction->fn->child;
|
|
if (type_is_invalid(fn->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (fn->value.type->id != ZigTypeIdFn) {
|
|
ir_add_error(ira, fn,
|
|
buf_sprintf("expected function, found '%s'", buf_ptr(&fn->value.type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *result = ir_build_frame_size_gen(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node, fn);
|
|
result->value.type = ira->codegen->builtin_types.entry_usize;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstructionAlignOf *instruction) {
|
|
Error err;
|
|
IrInstruction *type_value = instruction->type_value->child;
|
|
if (type_is_invalid(type_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
ZigType *type_entry = ir_resolve_type(ira, type_value);
|
|
|
|
if ((err = type_resolve(ira->codegen, type_entry, ResolveStatusAlignmentKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
switch (type_entry->id) {
|
|
case ZigTypeIdInvalid:
|
|
zig_unreachable();
|
|
case ZigTypeIdMetaType:
|
|
case ZigTypeIdUnreachable:
|
|
case ZigTypeIdComptimeFloat:
|
|
case ZigTypeIdComptimeInt:
|
|
case ZigTypeIdEnumLiteral:
|
|
case ZigTypeIdUndefined:
|
|
case ZigTypeIdNull:
|
|
case ZigTypeIdBoundFn:
|
|
case ZigTypeIdArgTuple:
|
|
case ZigTypeIdVoid:
|
|
case ZigTypeIdOpaque:
|
|
ir_add_error(ira, instruction->type_value,
|
|
buf_sprintf("no align available for type '%s'", buf_ptr(&type_entry->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
case ZigTypeIdBool:
|
|
case ZigTypeIdInt:
|
|
case ZigTypeIdFloat:
|
|
case ZigTypeIdPointer:
|
|
case ZigTypeIdArray:
|
|
case ZigTypeIdStruct:
|
|
case ZigTypeIdOptional:
|
|
case ZigTypeIdErrorUnion:
|
|
case ZigTypeIdErrorSet:
|
|
case ZigTypeIdEnum:
|
|
case ZigTypeIdUnion:
|
|
case ZigTypeIdFn:
|
|
case ZigTypeIdVector:
|
|
case ZigTypeIdCoroFrame:
|
|
case ZigTypeIdAnyFrame:
|
|
{
|
|
uint64_t align_in_bytes = get_abi_alignment(ira->codegen, type_entry);
|
|
return ir_const_unsigned(ira, &instruction->base, align_in_bytes);
|
|
}
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInstructionOverflowOp *instruction) {
|
|
Error err;
|
|
|
|
IrInstruction *type_value = instruction->type_value->child;
|
|
if (type_is_invalid(type_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *dest_type = ir_resolve_type(ira, type_value);
|
|
if (type_is_invalid(dest_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (dest_type->id != ZigTypeIdInt) {
|
|
ir_add_error(ira, type_value,
|
|
buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *op1 = instruction->op1->child;
|
|
if (type_is_invalid(op1->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, dest_type);
|
|
if (type_is_invalid(casted_op1->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *op2 = instruction->op2->child;
|
|
if (type_is_invalid(op2->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *casted_op2;
|
|
if (instruction->op == IrOverflowOpShl) {
|
|
ZigType *shift_amt_type = get_smallest_unsigned_int_type(ira->codegen,
|
|
dest_type->data.integral.bit_count - 1);
|
|
casted_op2 = ir_implicit_cast(ira, op2, shift_amt_type);
|
|
} else {
|
|
casted_op2 = ir_implicit_cast(ira, op2, dest_type);
|
|
}
|
|
if (type_is_invalid(casted_op2->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *result_ptr = instruction->result_ptr->child;
|
|
if (type_is_invalid(result_ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *expected_ptr_type;
|
|
if (result_ptr->value.type->id == ZigTypeIdPointer) {
|
|
uint32_t alignment;
|
|
if ((err = resolve_ptr_align(ira, result_ptr->value.type, &alignment)))
|
|
return ira->codegen->invalid_instruction;
|
|
expected_ptr_type = get_pointer_to_type_extra(ira->codegen, dest_type,
|
|
false, result_ptr->value.type->data.pointer.is_volatile,
|
|
PtrLenSingle,
|
|
alignment, 0, 0, false);
|
|
} else {
|
|
expected_ptr_type = get_pointer_to_type(ira->codegen, dest_type, false);
|
|
}
|
|
|
|
IrInstruction *casted_result_ptr = ir_implicit_cast(ira, result_ptr, expected_ptr_type);
|
|
if (type_is_invalid(casted_result_ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (casted_op1->value.special == ConstValSpecialStatic &&
|
|
casted_op2->value.special == ConstValSpecialStatic &&
|
|
casted_result_ptr->value.special == ConstValSpecialStatic)
|
|
{
|
|
BigInt *op1_bigint = &casted_op1->value.data.x_bigint;
|
|
BigInt *op2_bigint = &casted_op2->value.data.x_bigint;
|
|
ConstExprValue *pointee_val = const_ptr_pointee(ira, ira->codegen, &casted_result_ptr->value, casted_result_ptr->source_node);
|
|
if (pointee_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
BigInt *dest_bigint = &pointee_val->data.x_bigint;
|
|
switch (instruction->op) {
|
|
case IrOverflowOpAdd:
|
|
bigint_add(dest_bigint, op1_bigint, op2_bigint);
|
|
break;
|
|
case IrOverflowOpSub:
|
|
bigint_sub(dest_bigint, op1_bigint, op2_bigint);
|
|
break;
|
|
case IrOverflowOpMul:
|
|
bigint_mul(dest_bigint, op1_bigint, op2_bigint);
|
|
break;
|
|
case IrOverflowOpShl:
|
|
bigint_shl(dest_bigint, op1_bigint, op2_bigint);
|
|
break;
|
|
}
|
|
bool result_bool = false;
|
|
if (!bigint_fits_in_bits(dest_bigint, dest_type->data.integral.bit_count,
|
|
dest_type->data.integral.is_signed))
|
|
{
|
|
result_bool = true;
|
|
BigInt tmp_bigint;
|
|
bigint_init_bigint(&tmp_bigint, dest_bigint);
|
|
bigint_truncate(dest_bigint, &tmp_bigint, dest_type->data.integral.bit_count,
|
|
dest_type->data.integral.is_signed);
|
|
}
|
|
pointee_val->special = ConstValSpecialStatic;
|
|
return ir_const_bool(ira, &instruction->base, result_bool);
|
|
}
|
|
|
|
IrInstruction *result = ir_build_overflow_op(&ira->new_irb,
|
|
instruction->base.scope, instruction->base.source_node,
|
|
instruction->op, type_value, casted_op1, casted_op2, casted_result_ptr, dest_type);
|
|
result->value.type = ira->codegen->builtin_types.entry_bool;
|
|
return result;
|
|
}
|
|
|
|
static void ir_eval_mul_add(IrAnalyze *ira, IrInstructionMulAdd *source_instr, ZigType *float_type,
|
|
ConstExprValue *op1, ConstExprValue *op2, ConstExprValue *op3, ConstExprValue *out_val) {
|
|
if (float_type->id == ZigTypeIdComptimeFloat) {
|
|
f128M_mulAdd(&out_val->data.x_bigfloat.value, &op1->data.x_bigfloat.value, &op2->data.x_bigfloat.value,
|
|
&op3->data.x_bigfloat.value);
|
|
} else if (float_type->id == ZigTypeIdFloat) {
|
|
switch (float_type->data.floating.bit_count) {
|
|
case 16:
|
|
out_val->data.x_f16 = f16_mulAdd(op1->data.x_f16, op2->data.x_f16, op3->data.x_f16);
|
|
break;
|
|
case 32:
|
|
out_val->data.x_f32 = fmaf(op1->data.x_f32, op2->data.x_f32, op3->data.x_f32);
|
|
break;
|
|
case 64:
|
|
out_val->data.x_f64 = fma(op1->data.x_f64, op2->data.x_f64, op3->data.x_f64);
|
|
break;
|
|
case 128:
|
|
f128M_mulAdd(&op1->data.x_f128, &op2->data.x_f128, &op3->data.x_f128, &out_val->data.x_f128);
|
|
break;
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_mul_add(IrAnalyze *ira, IrInstructionMulAdd *instruction) {
|
|
IrInstruction *type_value = instruction->type_value->child;
|
|
if (type_is_invalid(type_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *expr_type = ir_resolve_type(ira, type_value);
|
|
if (type_is_invalid(expr_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
// Only allow float types, and vectors of floats.
|
|
ZigType *float_type = (expr_type->id == ZigTypeIdVector) ? expr_type->data.vector.elem_type : expr_type;
|
|
if (float_type->id != ZigTypeIdFloat) {
|
|
ir_add_error(ira, type_value,
|
|
buf_sprintf("expected float or vector of float type, found '%s'", buf_ptr(&float_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *op1 = instruction->op1->child;
|
|
if (type_is_invalid(op1->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, expr_type);
|
|
if (type_is_invalid(casted_op1->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *op2 = instruction->op2->child;
|
|
if (type_is_invalid(op2->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *casted_op2 = ir_implicit_cast(ira, op2, expr_type);
|
|
if (type_is_invalid(casted_op2->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *op3 = instruction->op3->child;
|
|
if (type_is_invalid(op3->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *casted_op3 = ir_implicit_cast(ira, op3, expr_type);
|
|
if (type_is_invalid(casted_op3->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (instr_is_comptime(casted_op1) &&
|
|
instr_is_comptime(casted_op2) &&
|
|
instr_is_comptime(casted_op3)) {
|
|
ConstExprValue *op1_const = ir_resolve_const(ira, casted_op1, UndefBad);
|
|
if (!op1_const)
|
|
return ira->codegen->invalid_instruction;
|
|
ConstExprValue *op2_const = ir_resolve_const(ira, casted_op2, UndefBad);
|
|
if (!op2_const)
|
|
return ira->codegen->invalid_instruction;
|
|
ConstExprValue *op3_const = ir_resolve_const(ira, casted_op3, UndefBad);
|
|
if (!op3_const)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *result = ir_const(ira, &instruction->base, expr_type);
|
|
ConstExprValue *out_val = &result->value;
|
|
|
|
if (expr_type->id == ZigTypeIdVector) {
|
|
expand_undef_array(ira->codegen, op1_const);
|
|
expand_undef_array(ira->codegen, op2_const);
|
|
expand_undef_array(ira->codegen, op3_const);
|
|
out_val->special = ConstValSpecialUndef;
|
|
expand_undef_array(ira->codegen, out_val);
|
|
size_t len = expr_type->data.vector.len;
|
|
for (size_t i = 0; i < len; i += 1) {
|
|
ConstExprValue *float_operand_op1 = &op1_const->data.x_array.data.s_none.elements[i];
|
|
ConstExprValue *float_operand_op2 = &op2_const->data.x_array.data.s_none.elements[i];
|
|
ConstExprValue *float_operand_op3 = &op3_const->data.x_array.data.s_none.elements[i];
|
|
ConstExprValue *float_out_val = &out_val->data.x_array.data.s_none.elements[i];
|
|
assert(float_operand_op1->type == float_type);
|
|
assert(float_operand_op2->type == float_type);
|
|
assert(float_operand_op3->type == float_type);
|
|
assert(float_out_val->type == float_type);
|
|
ir_eval_mul_add(ira, instruction, float_type,
|
|
op1_const, op2_const, op3_const, float_out_val);
|
|
float_out_val->type = float_type;
|
|
}
|
|
out_val->type = expr_type;
|
|
out_val->special = ConstValSpecialStatic;
|
|
} else {
|
|
ir_eval_mul_add(ira, instruction, float_type, op1_const, op2_const, op3_const, out_val);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
IrInstruction *result = ir_build_mul_add(&ira->new_irb,
|
|
instruction->base.scope, instruction->base.source_node,
|
|
type_value, casted_op1, casted_op2, casted_op3);
|
|
result->value.type = expr_type;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstructionTestErrSrc *instruction) {
|
|
IrInstruction *base_ptr = instruction->base_ptr->child;
|
|
if (type_is_invalid(base_ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *value;
|
|
if (instruction->base_ptr_is_payload) {
|
|
value = base_ptr;
|
|
} else {
|
|
value = ir_get_deref(ira, &instruction->base, base_ptr, nullptr);
|
|
}
|
|
|
|
ZigType *type_entry = value->value.type;
|
|
if (type_is_invalid(type_entry))
|
|
return ira->codegen->invalid_instruction;
|
|
if (type_entry->id == ZigTypeIdErrorUnion) {
|
|
if (instr_is_comptime(value)) {
|
|
ConstExprValue *err_union_val = ir_resolve_const(ira, value, UndefBad);
|
|
if (!err_union_val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (err_union_val->special != ConstValSpecialRuntime) {
|
|
ErrorTableEntry *err = err_union_val->data.x_err_union.error_set->data.x_err_set;
|
|
return ir_const_bool(ira, &instruction->base, (err != nullptr));
|
|
}
|
|
}
|
|
|
|
if (instruction->resolve_err_set) {
|
|
ZigType *err_set_type = type_entry->data.error_union.err_set_type;
|
|
if (!resolve_inferred_error_set(ira->codegen, err_set_type, instruction->base.source_node)) {
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
if (!type_is_global_error_set(err_set_type) &&
|
|
err_set_type->data.error_set.err_count == 0)
|
|
{
|
|
assert(err_set_type->data.error_set.infer_fn == nullptr);
|
|
return ir_const_bool(ira, &instruction->base, false);
|
|
}
|
|
}
|
|
|
|
return ir_build_test_err_gen(ira, &instruction->base, value);
|
|
} else if (type_entry->id == ZigTypeIdErrorSet) {
|
|
return ir_const_bool(ira, &instruction->base, true);
|
|
} else {
|
|
return ir_const_bool(ira, &instruction->base, false);
|
|
}
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_unwrap_err_code(IrAnalyze *ira, IrInstruction *source_instr,
|
|
IrInstruction *base_ptr, bool initializing)
|
|
{
|
|
ZigType *ptr_type = base_ptr->value.type;
|
|
|
|
// This will be a pointer type because unwrap err payload IR instruction operates on a pointer to a thing.
|
|
assert(ptr_type->id == ZigTypeIdPointer);
|
|
|
|
ZigType *type_entry = ptr_type->data.pointer.child_type;
|
|
if (type_is_invalid(type_entry))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (type_entry->id != ZigTypeIdErrorUnion) {
|
|
ir_add_error(ira, base_ptr,
|
|
buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
ZigType *err_set_type = type_entry->data.error_union.err_set_type;
|
|
ZigType *result_type = get_pointer_to_type_extra(ira->codegen, err_set_type,
|
|
ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, PtrLenSingle,
|
|
ptr_type->data.pointer.explicit_alignment, 0, 0, false);
|
|
|
|
if (instr_is_comptime(base_ptr)) {
|
|
ConstExprValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad);
|
|
if (!ptr_val)
|
|
return ira->codegen->invalid_instruction;
|
|
if (ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar &&
|
|
ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr)
|
|
{
|
|
ConstExprValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node);
|
|
if (err_union_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (initializing && err_union_val->special == ConstValSpecialUndef) {
|
|
ConstExprValue *vals = create_const_vals(2);
|
|
ConstExprValue *err_set_val = &vals[0];
|
|
ConstExprValue *payload_val = &vals[1];
|
|
|
|
err_set_val->special = ConstValSpecialUndef;
|
|
err_set_val->type = err_set_type;
|
|
err_set_val->parent.id = ConstParentIdErrUnionCode;
|
|
err_set_val->parent.data.p_err_union_code.err_union_val = err_union_val;
|
|
|
|
payload_val->special = ConstValSpecialUndef;
|
|
payload_val->type = type_entry->data.error_union.payload_type;
|
|
payload_val->parent.id = ConstParentIdErrUnionPayload;
|
|
payload_val->parent.data.p_err_union_payload.err_union_val = err_union_val;
|
|
|
|
err_union_val->special = ConstValSpecialStatic;
|
|
err_union_val->data.x_err_union.error_set = err_set_val;
|
|
err_union_val->data.x_err_union.payload = payload_val;
|
|
}
|
|
ir_assert(err_union_val->special != ConstValSpecialRuntime, source_instr);
|
|
|
|
IrInstruction *result;
|
|
if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) {
|
|
result = ir_build_unwrap_err_code(&ira->new_irb, source_instr->scope,
|
|
source_instr->source_node, base_ptr);
|
|
result->value.type = result_type;
|
|
result->value.special = ConstValSpecialStatic;
|
|
} else {
|
|
result = ir_const(ira, source_instr, result_type);
|
|
}
|
|
ConstExprValue *const_val = &result->value;
|
|
const_val->data.x_ptr.special = ConstPtrSpecialBaseErrorUnionCode;
|
|
const_val->data.x_ptr.data.base_err_union_code.err_union_val = err_union_val;
|
|
const_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
IrInstruction *result = ir_build_unwrap_err_code(&ira->new_irb,
|
|
source_instr->scope, source_instr->source_node, base_ptr);
|
|
result->value.type = result_type;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira,
|
|
IrInstructionUnwrapErrCode *instruction)
|
|
{
|
|
IrInstruction *base_ptr = instruction->err_union_ptr->child;
|
|
if (type_is_invalid(base_ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
return ir_analyze_unwrap_err_code(ira, &instruction->base, base_ptr, false);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_unwrap_error_payload(IrAnalyze *ira, IrInstruction *source_instr,
|
|
IrInstruction *base_ptr, bool safety_check_on, bool initializing)
|
|
{
|
|
ZigType *ptr_type = base_ptr->value.type;
|
|
|
|
// This will be a pointer type because unwrap err payload IR instruction operates on a pointer to a thing.
|
|
assert(ptr_type->id == ZigTypeIdPointer);
|
|
|
|
ZigType *type_entry = ptr_type->data.pointer.child_type;
|
|
if (type_is_invalid(type_entry))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (type_entry->id != ZigTypeIdErrorUnion) {
|
|
ir_add_error(ira, base_ptr,
|
|
buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
ZigType *payload_type = type_entry->data.error_union.payload_type;
|
|
if (type_is_invalid(payload_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *result_type = get_pointer_to_type_extra(ira->codegen, payload_type,
|
|
ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile,
|
|
PtrLenSingle, 0, 0, 0, false);
|
|
if (instr_is_comptime(base_ptr)) {
|
|
ConstExprValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad);
|
|
if (!ptr_val)
|
|
return ira->codegen->invalid_instruction;
|
|
if (ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar) {
|
|
ConstExprValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node);
|
|
if (err_union_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
if (initializing && err_union_val->special == ConstValSpecialUndef) {
|
|
ConstExprValue *vals = create_const_vals(2);
|
|
ConstExprValue *err_set_val = &vals[0];
|
|
ConstExprValue *payload_val = &vals[1];
|
|
|
|
err_set_val->special = ConstValSpecialStatic;
|
|
err_set_val->type = type_entry->data.error_union.err_set_type;
|
|
err_set_val->data.x_err_set = nullptr;
|
|
|
|
payload_val->special = ConstValSpecialUndef;
|
|
payload_val->type = payload_type;
|
|
|
|
err_union_val->special = ConstValSpecialStatic;
|
|
err_union_val->data.x_err_union.error_set = err_set_val;
|
|
err_union_val->data.x_err_union.payload = payload_val;
|
|
}
|
|
|
|
if (err_union_val->special != ConstValSpecialRuntime) {
|
|
ErrorTableEntry *err = err_union_val->data.x_err_union.error_set->data.x_err_set;
|
|
if (err != nullptr) {
|
|
ir_add_error(ira, source_instr,
|
|
buf_sprintf("caught unexpected error '%s'", buf_ptr(&err->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *result;
|
|
if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) {
|
|
result = ir_build_unwrap_err_payload(&ira->new_irb, source_instr->scope,
|
|
source_instr->source_node, base_ptr, safety_check_on, initializing);
|
|
result->value.type = result_type;
|
|
result->value.special = ConstValSpecialStatic;
|
|
} else {
|
|
result = ir_const(ira, source_instr, result_type);
|
|
}
|
|
result->value.data.x_ptr.special = ConstPtrSpecialRef;
|
|
result->value.data.x_ptr.data.ref.pointee = err_union_val->data.x_err_union.payload;
|
|
result->value.data.x_ptr.mut = ptr_val->data.x_ptr.mut;
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
IrInstruction *result = ir_build_unwrap_err_payload(&ira->new_irb, source_instr->scope,
|
|
source_instr->source_node, base_ptr, safety_check_on, initializing);
|
|
result->value.type = result_type;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira,
|
|
IrInstructionUnwrapErrPayload *instruction)
|
|
{
|
|
assert(instruction->value->child);
|
|
IrInstruction *value = instruction->value->child;
|
|
if (type_is_invalid(value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
return ir_analyze_unwrap_error_payload(ira, &instruction->base, value, instruction->safety_check_on, false);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstructionFnProto *instruction) {
|
|
AstNode *proto_node = instruction->base.source_node;
|
|
assert(proto_node->type == NodeTypeFnProto);
|
|
|
|
if (proto_node->data.fn_proto.auto_err_set) {
|
|
ir_add_error(ira, &instruction->base,
|
|
buf_sprintf("inferring error set of return type valid only for function definitions"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
FnTypeId fn_type_id = {0};
|
|
init_fn_type_id(&fn_type_id, proto_node, proto_node->data.fn_proto.params.length);
|
|
|
|
for (; fn_type_id.next_param_index < fn_type_id.param_count; fn_type_id.next_param_index += 1) {
|
|
AstNode *param_node = proto_node->data.fn_proto.params.at(fn_type_id.next_param_index);
|
|
assert(param_node->type == NodeTypeParamDecl);
|
|
|
|
bool param_is_var_args = param_node->data.param_decl.is_var_args;
|
|
if (param_is_var_args) {
|
|
if (fn_type_id.cc == CallingConventionC) {
|
|
fn_type_id.param_count = fn_type_id.next_param_index;
|
|
continue;
|
|
} else if (fn_type_id.cc == CallingConventionUnspecified) {
|
|
return ir_const_type(ira, &instruction->base, get_generic_fn_type(ira->codegen, &fn_type_id));
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index];
|
|
param_info->is_noalias = param_node->data.param_decl.is_noalias;
|
|
|
|
if (instruction->param_types[fn_type_id.next_param_index] == nullptr) {
|
|
param_info->type = nullptr;
|
|
return ir_const_type(ira, &instruction->base, get_generic_fn_type(ira->codegen, &fn_type_id));
|
|
} else {
|
|
IrInstruction *param_type_value = instruction->param_types[fn_type_id.next_param_index]->child;
|
|
if (type_is_invalid(param_type_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
ZigType *param_type = ir_resolve_type(ira, param_type_value);
|
|
switch (type_requires_comptime(ira->codegen, param_type)) {
|
|
case ReqCompTimeYes:
|
|
if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
|
|
ir_add_error(ira, param_type_value,
|
|
buf_sprintf("parameter of type '%s' not allowed in function with calling convention '%s'",
|
|
buf_ptr(¶m_type->name), calling_convention_name(fn_type_id.cc)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
param_info->type = param_type;
|
|
fn_type_id.next_param_index += 1;
|
|
return ir_const_type(ira, &instruction->base, get_generic_fn_type(ira->codegen, &fn_type_id));
|
|
case ReqCompTimeInvalid:
|
|
return ira->codegen->invalid_instruction;
|
|
case ReqCompTimeNo:
|
|
break;
|
|
}
|
|
if (!type_has_bits(param_type) && !calling_convention_allows_zig_types(fn_type_id.cc)) {
|
|
ir_add_error(ira, param_type_value,
|
|
buf_sprintf("parameter of type '%s' has 0 bits; not allowed in function with calling convention '%s'",
|
|
buf_ptr(¶m_type->name), calling_convention_name(fn_type_id.cc)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
param_info->type = param_type;
|
|
}
|
|
|
|
}
|
|
|
|
if (instruction->align_value != nullptr) {
|
|
if (!ir_resolve_align(ira, instruction->align_value->child, &fn_type_id.alignment))
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *return_type_value = instruction->return_type->child;
|
|
fn_type_id.return_type = ir_resolve_type(ira, return_type_value);
|
|
if (type_is_invalid(fn_type_id.return_type))
|
|
return ira->codegen->invalid_instruction;
|
|
if (fn_type_id.return_type->id == ZigTypeIdOpaque) {
|
|
ir_add_error(ira, instruction->return_type,
|
|
buf_sprintf("return type cannot be opaque"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
return ir_const_type(ira, &instruction->base, get_fn_type(ira->codegen, &fn_type_id));
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_test_comptime(IrAnalyze *ira, IrInstructionTestComptime *instruction) {
|
|
IrInstruction *value = instruction->value->child;
|
|
if (type_is_invalid(value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
return ir_const_bool(ira, &instruction->base, instr_is_comptime(value));
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
|
|
IrInstructionCheckSwitchProngs *instruction)
|
|
{
|
|
IrInstruction *target_value = instruction->target_value->child;
|
|
ZigType *switch_type = target_value->value.type;
|
|
if (type_is_invalid(switch_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (switch_type->id == ZigTypeIdEnum) {
|
|
HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> field_prev_uses = {};
|
|
field_prev_uses.init(switch_type->data.enumeration.src_field_count);
|
|
|
|
for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) {
|
|
IrInstructionCheckSwitchProngsRange *range = &instruction->ranges[range_i];
|
|
|
|
IrInstruction *start_value_uncasted = range->start->child;
|
|
if (type_is_invalid(start_value_uncasted->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
IrInstruction *start_value = ir_implicit_cast(ira, start_value_uncasted, switch_type);
|
|
if (type_is_invalid(start_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *end_value_uncasted = range->end->child;
|
|
if (type_is_invalid(end_value_uncasted->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
IrInstruction *end_value = ir_implicit_cast(ira, end_value_uncasted, switch_type);
|
|
if (type_is_invalid(end_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
BigInt start_index;
|
|
bigint_init_bigint(&start_index, &start_value->value.data.x_enum_tag);
|
|
|
|
assert(end_value->value.type->id == ZigTypeIdEnum);
|
|
BigInt end_index;
|
|
bigint_init_bigint(&end_index, &end_value->value.data.x_enum_tag);
|
|
|
|
BigInt field_index;
|
|
bigint_init_bigint(&field_index, &start_index);
|
|
for (;;) {
|
|
Cmp cmp = bigint_cmp(&field_index, &end_index);
|
|
if (cmp == CmpGT) {
|
|
break;
|
|
}
|
|
auto entry = field_prev_uses.put_unique(field_index, start_value->source_node);
|
|
if (entry) {
|
|
AstNode *prev_node = entry->value;
|
|
TypeEnumField *enum_field = find_enum_field_by_tag(switch_type, &field_index);
|
|
assert(enum_field != nullptr);
|
|
ErrorMsg *msg = ir_add_error(ira, start_value,
|
|
buf_sprintf("duplicate switch value: '%s.%s'", buf_ptr(&switch_type->name),
|
|
buf_ptr(enum_field->name)));
|
|
add_error_note(ira->codegen, msg, prev_node, buf_sprintf("other value is here"));
|
|
}
|
|
bigint_incr(&field_index);
|
|
}
|
|
}
|
|
if (!instruction->have_else_prong) {
|
|
for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) {
|
|
TypeEnumField *enum_field = &switch_type->data.enumeration.fields[i];
|
|
|
|
auto entry = field_prev_uses.maybe_get(enum_field->value);
|
|
if (!entry) {
|
|
ir_add_error(ira, &instruction->base,
|
|
buf_sprintf("enumeration value '%s.%s' not handled in switch", buf_ptr(&switch_type->name),
|
|
buf_ptr(enum_field->name)));
|
|
}
|
|
}
|
|
}
|
|
} else if (switch_type->id == ZigTypeIdErrorSet) {
|
|
if (!resolve_inferred_error_set(ira->codegen, switch_type, target_value->source_node)) {
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
AstNode **field_prev_uses = allocate<AstNode *>(ira->codegen->errors_by_index.length);
|
|
|
|
for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) {
|
|
IrInstructionCheckSwitchProngsRange *range = &instruction->ranges[range_i];
|
|
|
|
IrInstruction *start_value_uncasted = range->start->child;
|
|
if (type_is_invalid(start_value_uncasted->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
IrInstruction *start_value = ir_implicit_cast(ira, start_value_uncasted, switch_type);
|
|
if (type_is_invalid(start_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *end_value_uncasted = range->end->child;
|
|
if (type_is_invalid(end_value_uncasted->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
IrInstruction *end_value = ir_implicit_cast(ira, end_value_uncasted, switch_type);
|
|
if (type_is_invalid(end_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ir_assert(start_value->value.type->id == ZigTypeIdErrorSet, &instruction->base);
|
|
uint32_t start_index = start_value->value.data.x_err_set->value;
|
|
|
|
ir_assert(end_value->value.type->id == ZigTypeIdErrorSet, &instruction->base);
|
|
uint32_t end_index = end_value->value.data.x_err_set->value;
|
|
|
|
if (start_index != end_index) {
|
|
ir_add_error(ira, end_value, buf_sprintf("ranges not allowed when switching on errors"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
AstNode *prev_node = field_prev_uses[start_index];
|
|
if (prev_node != nullptr) {
|
|
Buf *err_name = &ira->codegen->errors_by_index.at(start_index)->name;
|
|
ErrorMsg *msg = ir_add_error(ira, start_value,
|
|
buf_sprintf("duplicate switch value: '%s.%s'", buf_ptr(&switch_type->name), buf_ptr(err_name)));
|
|
add_error_note(ira->codegen, msg, prev_node, buf_sprintf("other value is here"));
|
|
}
|
|
field_prev_uses[start_index] = start_value->source_node;
|
|
}
|
|
if (!instruction->have_else_prong) {
|
|
if (type_is_global_error_set(switch_type)) {
|
|
ir_add_error(ira, &instruction->base,
|
|
buf_sprintf("else prong required when switching on type 'anyerror'"));
|
|
return ira->codegen->invalid_instruction;
|
|
} else {
|
|
for (uint32_t i = 0; i < switch_type->data.error_set.err_count; i += 1) {
|
|
ErrorTableEntry *err_entry = switch_type->data.error_set.errors[i];
|
|
|
|
AstNode *prev_node = field_prev_uses[err_entry->value];
|
|
if (prev_node == nullptr) {
|
|
ir_add_error(ira, &instruction->base,
|
|
buf_sprintf("error.%s not handled in switch", buf_ptr(&err_entry->name)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
free(field_prev_uses);
|
|
} else if (switch_type->id == ZigTypeIdInt) {
|
|
RangeSet rs = {0};
|
|
for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) {
|
|
IrInstructionCheckSwitchProngsRange *range = &instruction->ranges[range_i];
|
|
|
|
IrInstruction *start_value = range->start->child;
|
|
if (type_is_invalid(start_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
IrInstruction *casted_start_value = ir_implicit_cast(ira, start_value, switch_type);
|
|
if (type_is_invalid(casted_start_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *end_value = range->end->child;
|
|
if (type_is_invalid(end_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
IrInstruction *casted_end_value = ir_implicit_cast(ira, end_value, switch_type);
|
|
if (type_is_invalid(casted_end_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ConstExprValue *start_val = ir_resolve_const(ira, casted_start_value, UndefBad);
|
|
if (!start_val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ConstExprValue *end_val = ir_resolve_const(ira, casted_end_value, UndefBad);
|
|
if (!end_val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
assert(start_val->type->id == ZigTypeIdInt || start_val->type->id == ZigTypeIdComptimeInt);
|
|
assert(end_val->type->id == ZigTypeIdInt || end_val->type->id == ZigTypeIdComptimeInt);
|
|
AstNode *prev_node = rangeset_add_range(&rs, &start_val->data.x_bigint, &end_val->data.x_bigint,
|
|
start_value->source_node);
|
|
if (prev_node != nullptr) {
|
|
ErrorMsg *msg = ir_add_error(ira, start_value, buf_sprintf("duplicate switch value"));
|
|
add_error_note(ira->codegen, msg, prev_node, buf_sprintf("previous value is here"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
if (!instruction->have_else_prong) {
|
|
BigInt min_val;
|
|
eval_min_max_value_int(ira->codegen, switch_type, &min_val, false);
|
|
BigInt max_val;
|
|
eval_min_max_value_int(ira->codegen, switch_type, &max_val, true);
|
|
if (!rangeset_spans(&rs, &min_val, &max_val)) {
|
|
ir_add_error(ira, &instruction->base, buf_sprintf("switch must handle all possibilities"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
} else if (switch_type->id == ZigTypeIdBool) {
|
|
int seenTrue = 0;
|
|
int seenFalse = 0;
|
|
for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) {
|
|
IrInstructionCheckSwitchProngsRange *range = &instruction->ranges[range_i];
|
|
|
|
IrInstruction *value = range->start->child;
|
|
|
|
IrInstruction *casted_value = ir_implicit_cast(ira, value, switch_type);
|
|
if (type_is_invalid(casted_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ConstExprValue *const_expr_val = ir_resolve_const(ira, casted_value, UndefBad);
|
|
if (!const_expr_val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
assert(const_expr_val->type->id == ZigTypeIdBool);
|
|
|
|
if (const_expr_val->data.x_bool == true) {
|
|
seenTrue += 1;
|
|
} else {
|
|
seenFalse += 1;
|
|
}
|
|
|
|
if ((seenTrue > 1) || (seenFalse > 1)) {
|
|
ir_add_error(ira, value, buf_sprintf("duplicate switch value"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
if (((seenTrue < 1) || (seenFalse < 1)) && !instruction->have_else_prong) {
|
|
ir_add_error(ira, &instruction->base, buf_sprintf("switch must handle all possibilities"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
} else if (!instruction->have_else_prong) {
|
|
ir_add_error(ira, &instruction->base,
|
|
buf_sprintf("else prong required when switching on type '%s'", buf_ptr(&switch_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
return ir_const_void(ira, &instruction->base);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_check_statement_is_void(IrAnalyze *ira,
|
|
IrInstructionCheckStatementIsVoid *instruction)
|
|
{
|
|
IrInstruction *statement_value = instruction->statement_value->child;
|
|
ZigType *statement_type = statement_value->value.type;
|
|
if (type_is_invalid(statement_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (statement_type->id != ZigTypeIdVoid && statement_type->id != ZigTypeIdUnreachable) {
|
|
ir_add_error(ira, &instruction->base, buf_sprintf("expression value is ignored"));
|
|
}
|
|
|
|
return ir_const_void(ira, &instruction->base);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_panic(IrAnalyze *ira, IrInstructionPanic *instruction) {
|
|
IrInstruction *msg = instruction->msg->child;
|
|
if (type_is_invalid(msg->value.type))
|
|
return ir_unreach_error(ira);
|
|
|
|
if (ir_should_inline(ira->new_irb.exec, instruction->base.scope)) {
|
|
ir_add_error(ira, &instruction->base, buf_sprintf("encountered @panic at compile-time"));
|
|
return ir_unreach_error(ira);
|
|
}
|
|
|
|
ZigType *u8_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,
|
|
true, false, PtrLenUnknown, 0, 0, 0, false);
|
|
ZigType *str_type = get_slice_type(ira->codegen, u8_ptr_type);
|
|
IrInstruction *casted_msg = ir_implicit_cast(ira, msg, str_type);
|
|
if (type_is_invalid(casted_msg->value.type))
|
|
return ir_unreach_error(ira);
|
|
|
|
IrInstruction *new_instruction = ir_build_panic(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node, casted_msg);
|
|
return ir_finish_anal(ira, new_instruction);
|
|
}
|
|
|
|
static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint32_t align_bytes, bool safety_check_on) {
|
|
Error err;
|
|
|
|
ZigType *target_type = target->value.type;
|
|
assert(!type_is_invalid(target_type));
|
|
|
|
ZigType *result_type;
|
|
uint32_t old_align_bytes;
|
|
|
|
if (target_type->id == ZigTypeIdPointer) {
|
|
result_type = adjust_ptr_align(ira->codegen, target_type, align_bytes);
|
|
if ((err = resolve_ptr_align(ira, target_type, &old_align_bytes)))
|
|
return ira->codegen->invalid_instruction;
|
|
} else if (target_type->id == ZigTypeIdFn) {
|
|
FnTypeId fn_type_id = target_type->data.fn.fn_type_id;
|
|
old_align_bytes = fn_type_id.alignment;
|
|
fn_type_id.alignment = align_bytes;
|
|
result_type = get_fn_type(ira->codegen, &fn_type_id);
|
|
} else if (target_type->id == ZigTypeIdOptional &&
|
|
target_type->data.maybe.child_type->id == ZigTypeIdPointer)
|
|
{
|
|
ZigType *ptr_type = target_type->data.maybe.child_type;
|
|
if ((err = resolve_ptr_align(ira, ptr_type, &old_align_bytes)))
|
|
return ira->codegen->invalid_instruction;
|
|
ZigType *better_ptr_type = adjust_ptr_align(ira->codegen, ptr_type, align_bytes);
|
|
|
|
result_type = get_optional_type(ira->codegen, better_ptr_type);
|
|
} else if (target_type->id == ZigTypeIdOptional &&
|
|
target_type->data.maybe.child_type->id == ZigTypeIdFn)
|
|
{
|
|
FnTypeId fn_type_id = target_type->data.maybe.child_type->data.fn.fn_type_id;
|
|
old_align_bytes = fn_type_id.alignment;
|
|
fn_type_id.alignment = align_bytes;
|
|
ZigType *fn_type = get_fn_type(ira->codegen, &fn_type_id);
|
|
result_type = get_optional_type(ira->codegen, fn_type);
|
|
} else if (is_slice(target_type)) {
|
|
ZigType *slice_ptr_type = target_type->data.structure.fields[slice_ptr_index].type_entry;
|
|
if ((err = resolve_ptr_align(ira, slice_ptr_type, &old_align_bytes)))
|
|
return ira->codegen->invalid_instruction;
|
|
ZigType *result_ptr_type = adjust_ptr_align(ira->codegen, slice_ptr_type, align_bytes);
|
|
result_type = get_slice_type(ira->codegen, result_ptr_type);
|
|
} else {
|
|
ir_add_error(ira, target,
|
|
buf_sprintf("expected pointer or slice, found '%s'", buf_ptr(&target_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (instr_is_comptime(target)) {
|
|
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
|
|
if (!val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr &&
|
|
val->data.x_ptr.data.hard_coded_addr.addr % align_bytes != 0)
|
|
{
|
|
ir_add_error(ira, target,
|
|
buf_sprintf("pointer address 0x%" ZIG_PRI_x64 " is not aligned to %" PRIu32 " bytes",
|
|
val->data.x_ptr.data.hard_coded_addr.addr, align_bytes));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *result = ir_const(ira, target, result_type);
|
|
copy_const_val(&result->value, val, true);
|
|
result->value.type = result_type;
|
|
return result;
|
|
}
|
|
|
|
IrInstruction *result;
|
|
if (safety_check_on && align_bytes > old_align_bytes && align_bytes != 1) {
|
|
result = ir_build_align_cast(&ira->new_irb, target->scope, target->source_node, nullptr, target);
|
|
} else {
|
|
result = ir_build_cast(&ira->new_irb, target->scope, target->source_node, result_type, target, CastOpNoop);
|
|
}
|
|
result->value.type = result_type;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *ptr,
|
|
ZigType *dest_type, IrInstruction *dest_type_src, bool safety_check_on)
|
|
{
|
|
Error err;
|
|
|
|
ZigType *src_type = ptr->value.type;
|
|
assert(!type_is_invalid(src_type));
|
|
|
|
// We have a check for zero bits later so we use get_src_ptr_type to
|
|
// validate src_type and dest_type.
|
|
|
|
ZigType *src_ptr_type = get_src_ptr_type(src_type);
|
|
if (src_ptr_type == nullptr) {
|
|
ir_add_error(ira, ptr, buf_sprintf("expected pointer, found '%s'", buf_ptr(&src_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
ZigType *dest_ptr_type = get_src_ptr_type(dest_type);
|
|
if (dest_ptr_type == nullptr) {
|
|
ir_add_error(ira, dest_type_src,
|
|
buf_sprintf("expected pointer, found '%s'", buf_ptr(&dest_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (get_ptr_const(src_type) && !get_ptr_const(dest_type)) {
|
|
ir_add_error(ira, source_instr, buf_sprintf("cast discards const qualifier"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
uint32_t src_align_bytes;
|
|
if ((err = resolve_ptr_align(ira, src_type, &src_align_bytes)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
uint32_t dest_align_bytes;
|
|
if ((err = resolve_ptr_align(ira, dest_type, &dest_align_bytes)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (instr_is_comptime(ptr)) {
|
|
bool dest_allows_addr_zero = ptr_allows_addr_zero(dest_type);
|
|
UndefAllowed is_undef_allowed = dest_allows_addr_zero ? UndefOk : UndefBad;
|
|
ConstExprValue *val = ir_resolve_const(ira, ptr, is_undef_allowed);
|
|
if (!val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (val->special == ConstValSpecialStatic) {
|
|
bool is_addr_zero = val->data.x_ptr.special == ConstPtrSpecialNull ||
|
|
(val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr &&
|
|
val->data.x_ptr.data.hard_coded_addr.addr == 0);
|
|
if (is_addr_zero && !dest_allows_addr_zero) {
|
|
ir_add_error(ira, source_instr,
|
|
buf_sprintf("null pointer casted to type '%s'", buf_ptr(&dest_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
|
|
IrInstruction *result;
|
|
if (ptr->value.data.x_ptr.mut == ConstPtrMutInfer) {
|
|
result = ir_build_ptr_cast_gen(ira, source_instr, dest_type, ptr, safety_check_on);
|
|
} else {
|
|
result = ir_const(ira, source_instr, dest_type);
|
|
}
|
|
copy_const_val(&result->value, val, true);
|
|
result->value.type = dest_type;
|
|
|
|
// Keep the bigger alignment, it can only help-
|
|
// unless the target is zero bits.
|
|
if (src_align_bytes > dest_align_bytes && type_has_bits(dest_type)) {
|
|
result = ir_align_cast(ira, result, src_align_bytes, false);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
if (dest_align_bytes > src_align_bytes) {
|
|
ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("cast increases pointer alignment"));
|
|
add_error_note(ira->codegen, msg, ptr->source_node,
|
|
buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&src_type->name), src_align_bytes));
|
|
add_error_note(ira->codegen, msg, dest_type_src->source_node,
|
|
buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&dest_type->name), dest_align_bytes));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *casted_ptr = ir_build_ptr_cast_gen(ira, source_instr, dest_type, ptr, safety_check_on);
|
|
|
|
if (type_has_bits(dest_type) && !type_has_bits(src_type)) {
|
|
ErrorMsg *msg = ir_add_error(ira, source_instr,
|
|
buf_sprintf("'%s' and '%s' do not have the same in-memory representation",
|
|
buf_ptr(&src_type->name), buf_ptr(&dest_type->name)));
|
|
add_error_note(ira->codegen, msg, ptr->source_node,
|
|
buf_sprintf("'%s' has no in-memory bits", buf_ptr(&src_type->name)));
|
|
add_error_note(ira->codegen, msg, dest_type_src->source_node,
|
|
buf_sprintf("'%s' has in-memory bits", buf_ptr(&dest_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
// Keep the bigger alignment, it can only help-
|
|
// unless the target is zero bits.
|
|
IrInstruction *result;
|
|
if (src_align_bytes > dest_align_bytes && type_has_bits(dest_type)) {
|
|
result = ir_align_cast(ira, casted_ptr, src_align_bytes, false);
|
|
if (type_is_invalid(result->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
} else {
|
|
result = casted_ptr;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstructionPtrCastSrc *instruction) {
|
|
IrInstruction *dest_type_value = instruction->dest_type->child;
|
|
ZigType *dest_type = ir_resolve_type(ira, dest_type_value);
|
|
if (type_is_invalid(dest_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *ptr = instruction->ptr->child;
|
|
ZigType *src_type = ptr->value.type;
|
|
if (type_is_invalid(src_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
return ir_analyze_ptr_cast(ira, &instruction->base, ptr, dest_type, dest_type_value,
|
|
instruction->safety_check_on);
|
|
}
|
|
|
|
static void buf_write_value_bytes_array(CodeGen *codegen, uint8_t *buf, ConstExprValue *val, size_t len) {
|
|
size_t buf_i = 0;
|
|
// TODO optimize the buf case
|
|
expand_undef_array(codegen, val);
|
|
for (size_t elem_i = 0; elem_i < val->type->data.array.len; elem_i += 1) {
|
|
ConstExprValue *elem = &val->data.x_array.data.s_none.elements[elem_i];
|
|
buf_write_value_bytes(codegen, &buf[buf_i], elem);
|
|
buf_i += type_size(codegen, elem->type);
|
|
}
|
|
}
|
|
|
|
static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue *val) {
|
|
if (val->special == ConstValSpecialUndef)
|
|
val->special = ConstValSpecialStatic;
|
|
assert(val->special == ConstValSpecialStatic);
|
|
switch (val->type->id) {
|
|
case ZigTypeIdInvalid:
|
|
case ZigTypeIdMetaType:
|
|
case ZigTypeIdOpaque:
|
|
case ZigTypeIdBoundFn:
|
|
case ZigTypeIdArgTuple:
|
|
case ZigTypeIdUnreachable:
|
|
case ZigTypeIdComptimeFloat:
|
|
case ZigTypeIdComptimeInt:
|
|
case ZigTypeIdEnumLiteral:
|
|
case ZigTypeIdUndefined:
|
|
case ZigTypeIdNull:
|
|
case ZigTypeIdErrorUnion:
|
|
case ZigTypeIdErrorSet:
|
|
zig_unreachable();
|
|
case ZigTypeIdVoid:
|
|
return;
|
|
case ZigTypeIdBool:
|
|
buf[0] = val->data.x_bool ? 1 : 0;
|
|
return;
|
|
case ZigTypeIdInt:
|
|
bigint_write_twos_complement(&val->data.x_bigint, buf, val->type->data.integral.bit_count,
|
|
codegen->is_big_endian);
|
|
return;
|
|
case ZigTypeIdEnum:
|
|
bigint_write_twos_complement(&val->data.x_enum_tag, buf,
|
|
val->type->data.enumeration.tag_int_type->data.integral.bit_count,
|
|
codegen->is_big_endian);
|
|
return;
|
|
case ZigTypeIdFloat:
|
|
float_write_ieee597(val, buf, codegen->is_big_endian);
|
|
return;
|
|
case ZigTypeIdPointer:
|
|
if (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) {
|
|
BigInt bn;
|
|
bigint_init_unsigned(&bn, val->data.x_ptr.data.hard_coded_addr.addr);
|
|
bigint_write_twos_complement(&bn, buf, codegen->builtin_types.entry_usize->data.integral.bit_count, codegen->is_big_endian);
|
|
return;
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
case ZigTypeIdArray:
|
|
return buf_write_value_bytes_array(codegen, buf, val, val->type->data.array.len);
|
|
case ZigTypeIdVector:
|
|
return buf_write_value_bytes_array(codegen, buf, val, val->type->data.vector.len);
|
|
case ZigTypeIdStruct:
|
|
switch (val->type->data.structure.layout) {
|
|
case ContainerLayoutAuto:
|
|
zig_unreachable();
|
|
case ContainerLayoutExtern: {
|
|
size_t src_field_count = val->type->data.structure.src_field_count;
|
|
for (size_t field_i = 0; field_i < src_field_count; field_i += 1) {
|
|
TypeStructField *struct_field = &val->type->data.structure.fields[field_i];
|
|
if (struct_field->gen_index == SIZE_MAX)
|
|
continue;
|
|
ConstExprValue *field_val = &val->data.x_struct.fields[field_i];
|
|
size_t offset = struct_field->offset;
|
|
buf_write_value_bytes(codegen, buf + offset, field_val);
|
|
}
|
|
return;
|
|
}
|
|
case ContainerLayoutPacked: {
|
|
size_t src_field_count = val->type->data.structure.src_field_count;
|
|
size_t gen_field_count = val->type->data.structure.gen_field_count;
|
|
size_t gen_i = 0;
|
|
size_t src_i = 0;
|
|
size_t offset = 0;
|
|
bool is_big_endian = codegen->is_big_endian;
|
|
uint8_t child_buf_prealloc[16];
|
|
size_t child_buf_len = 16;
|
|
uint8_t *child_buf = child_buf_prealloc;
|
|
while (gen_i < gen_field_count) {
|
|
size_t big_int_byte_count = val->type->data.structure.host_int_bytes[gen_i];
|
|
if (big_int_byte_count > child_buf_len) {
|
|
child_buf = allocate_nonzero<uint8_t>(big_int_byte_count);
|
|
child_buf_len = big_int_byte_count;
|
|
}
|
|
BigInt big_int;
|
|
bigint_init_unsigned(&big_int, 0);
|
|
size_t used_bits = 0;
|
|
while (src_i < src_field_count) {
|
|
TypeStructField *field = &val->type->data.structure.fields[src_i];
|
|
assert(field->gen_index != SIZE_MAX);
|
|
if (field->gen_index != gen_i)
|
|
break;
|
|
uint32_t packed_bits_size = type_size_bits(codegen, field->type_entry);
|
|
buf_write_value_bytes(codegen, child_buf, &val->data.x_struct.fields[src_i]);
|
|
BigInt child_val;
|
|
bigint_read_twos_complement(&child_val, child_buf, packed_bits_size, is_big_endian,
|
|
false);
|
|
if (is_big_endian) {
|
|
BigInt shift_amt;
|
|
bigint_init_unsigned(&shift_amt, packed_bits_size);
|
|
BigInt shifted;
|
|
bigint_shl(&shifted, &big_int, &shift_amt);
|
|
bigint_or(&big_int, &shifted, &child_val);
|
|
} else {
|
|
BigInt shift_amt;
|
|
bigint_init_unsigned(&shift_amt, used_bits);
|
|
BigInt child_val_shifted;
|
|
bigint_shl(&child_val_shifted, &child_val, &shift_amt);
|
|
BigInt tmp;
|
|
bigint_or(&tmp, &big_int, &child_val_shifted);
|
|
big_int = tmp;
|
|
used_bits += packed_bits_size;
|
|
}
|
|
src_i += 1;
|
|
}
|
|
bigint_write_twos_complement(&big_int, buf + offset, big_int_byte_count * 8, is_big_endian);
|
|
offset += big_int_byte_count;
|
|
gen_i += 1;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
case ZigTypeIdOptional:
|
|
zig_panic("TODO buf_write_value_bytes maybe type");
|
|
case ZigTypeIdFn:
|
|
zig_panic("TODO buf_write_value_bytes fn type");
|
|
case ZigTypeIdUnion:
|
|
zig_panic("TODO buf_write_value_bytes union type");
|
|
case ZigTypeIdCoroFrame:
|
|
zig_panic("TODO buf_write_value_bytes async fn frame type");
|
|
case ZigTypeIdAnyFrame:
|
|
zig_panic("TODO buf_write_value_bytes anyframe type");
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
static Error buf_read_value_bytes_array(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf,
|
|
ConstExprValue *val, ZigType *elem_type, size_t len)
|
|
{
|
|
Error err;
|
|
uint64_t elem_size = type_size(codegen, elem_type);
|
|
|
|
switch (val->data.x_array.special) {
|
|
case ConstArraySpecialNone:
|
|
val->data.x_array.data.s_none.elements = create_const_vals(len);
|
|
for (size_t i = 0; i < len; i++) {
|
|
ConstExprValue *elem = &val->data.x_array.data.s_none.elements[i];
|
|
elem->special = ConstValSpecialStatic;
|
|
elem->type = elem_type;
|
|
if ((err = buf_read_value_bytes(ira, codegen, source_node, buf + (elem_size * i), elem)))
|
|
return err;
|
|
}
|
|
return ErrorNone;
|
|
case ConstArraySpecialUndef:
|
|
zig_panic("TODO buf_read_value_bytes ConstArraySpecialUndef array type");
|
|
case ConstArraySpecialBuf:
|
|
zig_panic("TODO buf_read_value_bytes ConstArraySpecialBuf array type");
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf, ConstExprValue *val) {
|
|
Error err;
|
|
src_assert(val->special == ConstValSpecialStatic, source_node);
|
|
switch (val->type->id) {
|
|
case ZigTypeIdInvalid:
|
|
case ZigTypeIdMetaType:
|
|
case ZigTypeIdOpaque:
|
|
case ZigTypeIdBoundFn:
|
|
case ZigTypeIdArgTuple:
|
|
case ZigTypeIdUnreachable:
|
|
case ZigTypeIdComptimeFloat:
|
|
case ZigTypeIdComptimeInt:
|
|
case ZigTypeIdEnumLiteral:
|
|
case ZigTypeIdUndefined:
|
|
case ZigTypeIdNull:
|
|
zig_unreachable();
|
|
case ZigTypeIdVoid:
|
|
return ErrorNone;
|
|
case ZigTypeIdBool:
|
|
val->data.x_bool = (buf[0] != 0);
|
|
return ErrorNone;
|
|
case ZigTypeIdInt:
|
|
bigint_read_twos_complement(&val->data.x_bigint, buf, val->type->data.integral.bit_count,
|
|
codegen->is_big_endian, val->type->data.integral.is_signed);
|
|
return ErrorNone;
|
|
case ZigTypeIdFloat:
|
|
float_read_ieee597(val, buf, codegen->is_big_endian);
|
|
return ErrorNone;
|
|
case ZigTypeIdPointer:
|
|
{
|
|
val->data.x_ptr.special = ConstPtrSpecialHardCodedAddr;
|
|
BigInt bn;
|
|
bigint_read_twos_complement(&bn, buf, codegen->builtin_types.entry_usize->data.integral.bit_count,
|
|
codegen->is_big_endian, false);
|
|
val->data.x_ptr.data.hard_coded_addr.addr = bigint_as_unsigned(&bn);
|
|
return ErrorNone;
|
|
}
|
|
case ZigTypeIdArray:
|
|
return buf_read_value_bytes_array(ira, codegen, source_node, buf, val, val->type->data.array.child_type,
|
|
val->type->data.array.len);
|
|
case ZigTypeIdVector:
|
|
return buf_read_value_bytes_array(ira, codegen, source_node, buf, val, val->type->data.vector.elem_type,
|
|
val->type->data.vector.len);
|
|
case ZigTypeIdEnum:
|
|
switch (val->type->data.enumeration.layout) {
|
|
case ContainerLayoutAuto:
|
|
zig_panic("TODO buf_read_value_bytes enum auto");
|
|
case ContainerLayoutPacked:
|
|
zig_panic("TODO buf_read_value_bytes enum packed");
|
|
case ContainerLayoutExtern: {
|
|
ZigType *tag_int_type = val->type->data.enumeration.tag_int_type;
|
|
src_assert(tag_int_type->id == ZigTypeIdInt, source_node);
|
|
bigint_read_twos_complement(&val->data.x_enum_tag, buf, tag_int_type->data.integral.bit_count,
|
|
codegen->is_big_endian, tag_int_type->data.integral.is_signed);
|
|
return ErrorNone;
|
|
}
|
|
}
|
|
zig_unreachable();
|
|
case ZigTypeIdStruct:
|
|
switch (val->type->data.structure.layout) {
|
|
case ContainerLayoutAuto: {
|
|
ErrorMsg *msg = opt_ir_add_error_node(ira, codegen, source_node,
|
|
buf_sprintf("non-extern, non-packed struct '%s' cannot have its bytes reinterpreted",
|
|
buf_ptr(&val->type->name)));
|
|
add_error_note(codegen, msg, val->type->data.structure.decl_node,
|
|
buf_sprintf("declared here"));
|
|
return ErrorSemanticAnalyzeFail;
|
|
}
|
|
case ContainerLayoutExtern: {
|
|
size_t src_field_count = val->type->data.structure.src_field_count;
|
|
val->data.x_struct.fields = create_const_vals(src_field_count);
|
|
for (size_t field_i = 0; field_i < src_field_count; field_i += 1) {
|
|
ConstExprValue *field_val = &val->data.x_struct.fields[field_i];
|
|
field_val->special = ConstValSpecialStatic;
|
|
TypeStructField *struct_field = &val->type->data.structure.fields[field_i];
|
|
field_val->type = struct_field->type_entry;
|
|
if (struct_field->gen_index == SIZE_MAX)
|
|
continue;
|
|
size_t offset = struct_field->offset;
|
|
uint8_t *new_buf = buf + offset;
|
|
if ((err = buf_read_value_bytes(ira, codegen, source_node, new_buf, field_val)))
|
|
return err;
|
|
}
|
|
return ErrorNone;
|
|
}
|
|
case ContainerLayoutPacked: {
|
|
size_t src_field_count = val->type->data.structure.src_field_count;
|
|
val->data.x_struct.fields = create_const_vals(src_field_count);
|
|
size_t gen_field_count = val->type->data.structure.gen_field_count;
|
|
size_t gen_i = 0;
|
|
size_t src_i = 0;
|
|
size_t offset = 0;
|
|
bool is_big_endian = codegen->is_big_endian;
|
|
uint8_t child_buf_prealloc[16];
|
|
size_t child_buf_len = 16;
|
|
uint8_t *child_buf = child_buf_prealloc;
|
|
while (gen_i < gen_field_count) {
|
|
size_t big_int_byte_count = val->type->data.structure.host_int_bytes[gen_i];
|
|
if (big_int_byte_count > child_buf_len) {
|
|
child_buf = allocate_nonzero<uint8_t>(big_int_byte_count);
|
|
child_buf_len = big_int_byte_count;
|
|
}
|
|
BigInt big_int;
|
|
bigint_read_twos_complement(&big_int, buf + offset, big_int_byte_count * 8, is_big_endian, false);
|
|
while (src_i < src_field_count) {
|
|
TypeStructField *field = &val->type->data.structure.fields[src_i];
|
|
src_assert(field->gen_index != SIZE_MAX, source_node);
|
|
if (field->gen_index != gen_i)
|
|
break;
|
|
ConstExprValue *field_val = &val->data.x_struct.fields[src_i];
|
|
field_val->special = ConstValSpecialStatic;
|
|
field_val->type = field->type_entry;
|
|
uint32_t packed_bits_size = type_size_bits(codegen, field->type_entry);
|
|
|
|
BigInt child_val;
|
|
if (is_big_endian) {
|
|
zig_panic("TODO buf_read_value_bytes packed struct big endian");
|
|
} else {
|
|
BigInt packed_bits_size_bi;
|
|
bigint_init_unsigned(&packed_bits_size_bi, packed_bits_size);
|
|
bigint_truncate(&child_val, &big_int, packed_bits_size, false);
|
|
BigInt tmp;
|
|
bigint_shr(&tmp, &big_int, &packed_bits_size_bi);
|
|
big_int = tmp;
|
|
}
|
|
|
|
bigint_write_twos_complement(&child_val, child_buf, big_int_byte_count * 8, is_big_endian);
|
|
if ((err = buf_read_value_bytes(ira, codegen, source_node, child_buf, field_val))) {
|
|
return err;
|
|
}
|
|
|
|
src_i += 1;
|
|
}
|
|
offset += big_int_byte_count;
|
|
gen_i += 1;
|
|
}
|
|
return ErrorNone;
|
|
}
|
|
}
|
|
zig_unreachable();
|
|
case ZigTypeIdOptional:
|
|
zig_panic("TODO buf_read_value_bytes maybe type");
|
|
case ZigTypeIdErrorUnion:
|
|
zig_panic("TODO buf_read_value_bytes error union");
|
|
case ZigTypeIdErrorSet:
|
|
zig_panic("TODO buf_read_value_bytes pure error type");
|
|
case ZigTypeIdFn:
|
|
zig_panic("TODO buf_read_value_bytes fn type");
|
|
case ZigTypeIdUnion:
|
|
zig_panic("TODO buf_read_value_bytes union type");
|
|
case ZigTypeIdCoroFrame:
|
|
zig_panic("TODO buf_read_value_bytes async fn frame type");
|
|
case ZigTypeIdAnyFrame:
|
|
zig_panic("TODO buf_read_value_bytes anyframe type");
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_bit_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value,
|
|
ZigType *dest_type)
|
|
{
|
|
Error err;
|
|
|
|
ZigType *src_type = value->value.type;
|
|
ir_assert(get_codegen_ptr_type(src_type) == nullptr, source_instr);
|
|
ir_assert(type_can_bit_cast(src_type), source_instr);
|
|
ir_assert(get_codegen_ptr_type(dest_type) == nullptr, source_instr);
|
|
ir_assert(type_can_bit_cast(dest_type), source_instr);
|
|
|
|
if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusSizeKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if ((err = type_resolve(ira->codegen, src_type, ResolveStatusSizeKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
|
|
uint64_t dest_size_bytes = type_size(ira->codegen, dest_type);
|
|
uint64_t src_size_bytes = type_size(ira->codegen, src_type);
|
|
if (dest_size_bytes != src_size_bytes) {
|
|
ir_add_error(ira, source_instr,
|
|
buf_sprintf("destination type '%s' has size %" ZIG_PRI_u64 " but source type '%s' has size %" ZIG_PRI_u64,
|
|
buf_ptr(&dest_type->name), dest_size_bytes,
|
|
buf_ptr(&src_type->name), src_size_bytes));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
uint64_t dest_size_bits = type_size_bits(ira->codegen, dest_type);
|
|
uint64_t src_size_bits = type_size_bits(ira->codegen, src_type);
|
|
if (dest_size_bits != src_size_bits) {
|
|
ir_add_error(ira, source_instr,
|
|
buf_sprintf("destination type '%s' has %" ZIG_PRI_u64 " bits but source type '%s' has %" ZIG_PRI_u64 " bits",
|
|
buf_ptr(&dest_type->name), dest_size_bits,
|
|
buf_ptr(&src_type->name), src_size_bits));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (instr_is_comptime(value)) {
|
|
ConstExprValue *val = ir_resolve_const(ira, value, UndefBad);
|
|
if (!val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *result = ir_const(ira, source_instr, dest_type);
|
|
uint8_t *buf = allocate_nonzero<uint8_t>(src_size_bytes);
|
|
buf_write_value_bytes(ira->codegen, buf, val);
|
|
if ((err = buf_read_value_bytes(ira, ira->codegen, source_instr->source_node, buf, &result->value)))
|
|
return ira->codegen->invalid_instruction;
|
|
return result;
|
|
}
|
|
|
|
return ir_build_bit_cast_gen(ira, source_instr, value, dest_type);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_int_to_ptr(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target,
|
|
ZigType *ptr_type)
|
|
{
|
|
ir_assert(get_src_ptr_type(ptr_type) != nullptr, source_instr);
|
|
ir_assert(type_has_bits(ptr_type), source_instr);
|
|
|
|
IrInstruction *casted_int = ir_implicit_cast(ira, target, ira->codegen->builtin_types.entry_usize);
|
|
if (type_is_invalid(casted_int->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (instr_is_comptime(casted_int)) {
|
|
ConstExprValue *val = ir_resolve_const(ira, casted_int, UndefBad);
|
|
if (!val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
uint64_t addr = bigint_as_unsigned(&val->data.x_bigint);
|
|
if (!ptr_allows_addr_zero(ptr_type) && addr == 0) {
|
|
ir_add_error(ira, source_instr,
|
|
buf_sprintf("pointer type '%s' does not allow address zero", buf_ptr(&ptr_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *result = ir_const(ira, source_instr, ptr_type);
|
|
result->value.data.x_ptr.special = ConstPtrSpecialHardCodedAddr;
|
|
result->value.data.x_ptr.mut = ConstPtrMutRuntimeVar;
|
|
result->value.data.x_ptr.data.hard_coded_addr.addr = addr;
|
|
return result;
|
|
}
|
|
|
|
IrInstruction *result = ir_build_int_to_ptr(&ira->new_irb, source_instr->scope,
|
|
source_instr->source_node, nullptr, casted_int);
|
|
result->value.type = ptr_type;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstructionIntToPtr *instruction) {
|
|
Error err;
|
|
IrInstruction *dest_type_value = instruction->dest_type->child;
|
|
ZigType *dest_type = ir_resolve_type(ira, dest_type_value);
|
|
if (type_is_invalid(dest_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
// We explicitly check for the size, so we can use get_src_ptr_type
|
|
if (get_src_ptr_type(dest_type) == nullptr) {
|
|
ir_add_error(ira, dest_type_value, buf_sprintf("expected pointer, found '%s'", buf_ptr(&dest_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
if (!type_has_bits(dest_type)) {
|
|
ir_add_error(ira, dest_type_value,
|
|
buf_sprintf("type '%s' has 0 bits and cannot store information", buf_ptr(&dest_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
|
|
IrInstruction *target = instruction->target->child;
|
|
if (type_is_invalid(target->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
return ir_analyze_int_to_ptr(ira, &instruction->base, target, dest_type);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_decl_ref(IrAnalyze *ira,
|
|
IrInstructionDeclRef *instruction)
|
|
{
|
|
IrInstruction *ref_instruction = ir_analyze_decl_ref(ira, &instruction->base, instruction->tld);
|
|
if (type_is_invalid(ref_instruction->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (instruction->lval == LValPtr) {
|
|
return ref_instruction;
|
|
} else {
|
|
return ir_get_deref(ira, &instruction->base, ref_instruction, nullptr);
|
|
}
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstructionPtrToInt *instruction) {
|
|
IrInstruction *target = instruction->target->child;
|
|
if (type_is_invalid(target->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *usize = ira->codegen->builtin_types.entry_usize;
|
|
|
|
// We check size explicitly so we can use get_src_ptr_type here.
|
|
if (get_src_ptr_type(target->value.type) == nullptr) {
|
|
ir_add_error(ira, target,
|
|
buf_sprintf("expected pointer, found '%s'", buf_ptr(&target->value.type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (!type_has_bits(target->value.type)) {
|
|
ir_add_error(ira, target,
|
|
buf_sprintf("pointer to size 0 type has no address"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (instr_is_comptime(target)) {
|
|
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
|
|
if (!val)
|
|
return ira->codegen->invalid_instruction;
|
|
if (val->type->id == ZigTypeIdPointer && val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) {
|
|
IrInstruction *result = ir_const(ira, &instruction->base, usize);
|
|
bigint_init_unsigned(&result->value.data.x_bigint, val->data.x_ptr.data.hard_coded_addr.addr);
|
|
result->value.type = usize;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
IrInstruction *result = ir_build_ptr_to_int(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node, target);
|
|
result->value.type = usize;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstructionPtrType *instruction) {
|
|
Error err;
|
|
ZigType *child_type = ir_resolve_type(ira, instruction->child_type->child);
|
|
if (type_is_invalid(child_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (child_type->id == ZigTypeIdUnreachable) {
|
|
ir_add_error(ira, &instruction->base, buf_sprintf("pointer to noreturn not allowed"));
|
|
return ira->codegen->invalid_instruction;
|
|
} else if (child_type->id == ZigTypeIdOpaque && instruction->ptr_len == PtrLenUnknown) {
|
|
ir_add_error(ira, &instruction->base, buf_sprintf("unknown-length pointer to opaque"));
|
|
return ira->codegen->invalid_instruction;
|
|
} else if (instruction->ptr_len == PtrLenC) {
|
|
if (!type_allowed_in_extern(ira->codegen, child_type)) {
|
|
ir_add_error(ira, &instruction->base,
|
|
buf_sprintf("C pointers cannot point to non-C-ABI-compatible type '%s'", buf_ptr(&child_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
} else if (child_type->id == ZigTypeIdOpaque) {
|
|
ir_add_error(ira, &instruction->base, buf_sprintf("C pointers cannot point opaque types"));
|
|
return ira->codegen->invalid_instruction;
|
|
} else if (instruction->is_allow_zero) {
|
|
ir_add_error(ira, &instruction->base, buf_sprintf("C pointers always allow address zero"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
|
|
uint32_t align_bytes;
|
|
if (instruction->align_value != nullptr) {
|
|
if (!ir_resolve_align(ira, instruction->align_value->child, &align_bytes))
|
|
return ira->codegen->invalid_instruction;
|
|
if ((err = type_resolve(ira->codegen, child_type, ResolveStatusAlignmentKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
if (!type_has_bits(child_type)) {
|
|
align_bytes = 0;
|
|
}
|
|
} else {
|
|
if ((err = type_resolve(ira->codegen, child_type, ResolveStatusZeroBitsKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
align_bytes = 0;
|
|
}
|
|
|
|
bool allow_zero = instruction->is_allow_zero || instruction->ptr_len == PtrLenC;
|
|
|
|
ZigType *result_type = get_pointer_to_type_extra(ira->codegen, child_type,
|
|
instruction->is_const, instruction->is_volatile,
|
|
instruction->ptr_len, align_bytes,
|
|
instruction->bit_offset_start, instruction->host_int_bytes, allow_zero);
|
|
return ir_const_type(ira, &instruction->base, result_type);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_align_cast(IrAnalyze *ira, IrInstructionAlignCast *instruction) {
|
|
uint32_t align_bytes;
|
|
IrInstruction *align_bytes_inst = instruction->align_bytes->child;
|
|
if (!ir_resolve_align(ira, align_bytes_inst, &align_bytes))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *target = instruction->target->child;
|
|
if (type_is_invalid(target->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *result = ir_align_cast(ira, target, align_bytes, true);
|
|
if (type_is_invalid(result->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_opaque_type(IrAnalyze *ira, IrInstructionOpaqueType *instruction) {
|
|
Buf *bare_name = buf_alloc();
|
|
Buf *full_name = get_anon_type_name(ira->codegen, ira->new_irb.exec, "opaque",
|
|
instruction->base.scope, instruction->base.source_node, bare_name);
|
|
ZigType *result_type = get_opaque_type(ira->codegen, instruction->base.scope, instruction->base.source_node,
|
|
buf_ptr(full_name), bare_name);
|
|
return ir_const_type(ira, &instruction->base, result_type);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_set_align_stack(IrAnalyze *ira, IrInstructionSetAlignStack *instruction) {
|
|
uint32_t align_bytes;
|
|
IrInstruction *align_bytes_inst = instruction->align_bytes->child;
|
|
if (!ir_resolve_align(ira, align_bytes_inst, &align_bytes))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (align_bytes > 256) {
|
|
ir_add_error(ira, &instruction->base, buf_sprintf("attempt to @setAlignStack(%" PRIu32 "); maximum is 256", align_bytes));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec);
|
|
if (fn_entry == nullptr) {
|
|
ir_add_error(ira, &instruction->base, buf_sprintf("@setAlignStack outside function"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
if (fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionNaked) {
|
|
ir_add_error(ira, &instruction->base, buf_sprintf("@setAlignStack in naked function"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (fn_entry->fn_inline == FnInlineAlways) {
|
|
ir_add_error(ira, &instruction->base, buf_sprintf("@setAlignStack in inline function"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (fn_entry->set_alignstack_node != nullptr) {
|
|
ErrorMsg *msg = ir_add_error_node(ira, instruction->base.source_node,
|
|
buf_sprintf("alignstack set twice"));
|
|
add_error_note(ira->codegen, msg, fn_entry->set_alignstack_node, buf_sprintf("first set here"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
fn_entry->set_alignstack_node = instruction->base.source_node;
|
|
fn_entry->alignstack_value = align_bytes;
|
|
|
|
return ir_const_void(ira, &instruction->base);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstructionArgType *instruction) {
|
|
IrInstruction *fn_type_inst = instruction->fn_type->child;
|
|
ZigType *fn_type = ir_resolve_type(ira, fn_type_inst);
|
|
if (type_is_invalid(fn_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *arg_index_inst = instruction->arg_index->child;
|
|
uint64_t arg_index;
|
|
if (!ir_resolve_usize(ira, arg_index_inst, &arg_index))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (fn_type->id != ZigTypeIdFn) {
|
|
ir_add_error(ira, fn_type_inst, buf_sprintf("expected function, found '%s'", buf_ptr(&fn_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
|
|
if (arg_index >= fn_type_id->param_count) {
|
|
ir_add_error(ira, arg_index_inst,
|
|
buf_sprintf("arg index %" ZIG_PRI_u64 " out of bounds; '%s' has %" ZIG_PRI_usize " arguments",
|
|
arg_index, buf_ptr(&fn_type->name), fn_type_id->param_count));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
ZigType *result_type = fn_type_id->param_info[arg_index].type;
|
|
if (result_type == nullptr) {
|
|
// Args are only unresolved if our function is generic.
|
|
ir_assert(fn_type->data.fn.is_generic, &instruction->base);
|
|
|
|
ir_add_error(ira, arg_index_inst,
|
|
buf_sprintf("@ArgType could not resolve the type of arg %" ZIG_PRI_u64 " because '%s' is generic",
|
|
arg_index, buf_ptr(&fn_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
return ir_const_type(ira, &instruction->base, result_type);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_tag_type(IrAnalyze *ira, IrInstructionTagType *instruction) {
|
|
Error err;
|
|
IrInstruction *target_inst = instruction->target->child;
|
|
ZigType *enum_type = ir_resolve_type(ira, target_inst);
|
|
if (type_is_invalid(enum_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (enum_type->id == ZigTypeIdEnum) {
|
|
if ((err = ensure_complete_type(ira->codegen, enum_type)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
return ir_const_type(ira, &instruction->base, enum_type->data.enumeration.tag_int_type);
|
|
} else if (enum_type->id == ZigTypeIdUnion) {
|
|
ZigType *tag_type = ir_resolve_union_tag_type(ira, instruction->target, enum_type);
|
|
if (type_is_invalid(tag_type))
|
|
return ira->codegen->invalid_instruction;
|
|
return ir_const_type(ira, &instruction->base, tag_type);
|
|
} else {
|
|
ir_add_error(ira, target_inst, buf_sprintf("expected enum or union, found '%s'",
|
|
buf_ptr(&enum_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_cancel(IrAnalyze *ira, IrInstructionCancel *instruction) {
|
|
zig_panic("TODO analyze cancel");
|
|
}
|
|
|
|
static ZigType *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op) {
|
|
ZigType *operand_type = ir_resolve_type(ira, op);
|
|
if (type_is_invalid(operand_type))
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
|
|
if (operand_type->id == ZigTypeIdInt) {
|
|
if (operand_type->data.integral.bit_count < 8) {
|
|
ir_add_error(ira, op,
|
|
buf_sprintf("expected integer type 8 bits or larger, found %" PRIu32 "-bit integer type",
|
|
operand_type->data.integral.bit_count));
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
}
|
|
uint32_t max_atomic_bits = target_arch_largest_atomic_bits(ira->codegen->zig_target->arch);
|
|
if (operand_type->data.integral.bit_count > max_atomic_bits) {
|
|
ir_add_error(ira, op,
|
|
buf_sprintf("expected %" PRIu32 "-bit integer type or smaller, found %" PRIu32 "-bit integer type",
|
|
max_atomic_bits, operand_type->data.integral.bit_count));
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
}
|
|
if (!is_power_of_2(operand_type->data.integral.bit_count)) {
|
|
ir_add_error(ira, op,
|
|
buf_sprintf("%" PRIu32 "-bit integer type is not a power of 2", operand_type->data.integral.bit_count));
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
}
|
|
} else if (get_codegen_ptr_type(operand_type) == nullptr) {
|
|
ir_add_error(ira, op,
|
|
buf_sprintf("expected integer or pointer type, found '%s'", buf_ptr(&operand_type->name)));
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
}
|
|
|
|
return operand_type;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstructionAtomicRmw *instruction) {
|
|
ZigType *operand_type = ir_resolve_atomic_operand_type(ira, instruction->operand_type->child);
|
|
if (type_is_invalid(operand_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *ptr_inst = instruction->ptr->child;
|
|
if (type_is_invalid(ptr_inst->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
// TODO let this be volatile
|
|
ZigType *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false);
|
|
IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type);
|
|
if (type_is_invalid(casted_ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
AtomicRmwOp op;
|
|
if (instruction->op == nullptr) {
|
|
op = instruction->resolved_op;
|
|
} else {
|
|
if (!ir_resolve_atomic_rmw_op(ira, instruction->op->child, &op)) {
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
|
|
IrInstruction *operand = instruction->operand->child;
|
|
if (type_is_invalid(operand->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *casted_operand = ir_implicit_cast(ira, operand, operand_type);
|
|
if (type_is_invalid(casted_operand->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
AtomicOrder ordering;
|
|
if (instruction->ordering == nullptr) {
|
|
ordering = instruction->resolved_ordering;
|
|
} else {
|
|
if (!ir_resolve_atomic_order(ira, instruction->ordering->child, &ordering))
|
|
return ira->codegen->invalid_instruction;
|
|
if (ordering == AtomicOrderUnordered) {
|
|
ir_add_error(ira, instruction->ordering,
|
|
buf_sprintf("@atomicRmw atomic ordering must not be Unordered"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
|
|
if (instr_is_comptime(casted_operand) && instr_is_comptime(casted_ptr) && casted_ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar)
|
|
{
|
|
zig_panic("TODO compile-time execution of atomicRmw");
|
|
}
|
|
|
|
IrInstruction *result = ir_build_atomic_rmw(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node, nullptr, casted_ptr, nullptr, casted_operand, nullptr,
|
|
op, ordering);
|
|
result->value.type = operand_type;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_atomic_load(IrAnalyze *ira, IrInstructionAtomicLoad *instruction) {
|
|
ZigType *operand_type = ir_resolve_atomic_operand_type(ira, instruction->operand_type->child);
|
|
if (type_is_invalid(operand_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *ptr_inst = instruction->ptr->child;
|
|
if (type_is_invalid(ptr_inst->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *ptr_type = get_pointer_to_type(ira->codegen, operand_type, true);
|
|
IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type);
|
|
if (type_is_invalid(casted_ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
AtomicOrder ordering;
|
|
if (instruction->ordering == nullptr) {
|
|
ordering = instruction->resolved_ordering;
|
|
} else {
|
|
if (!ir_resolve_atomic_order(ira, instruction->ordering->child, &ordering))
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (ordering == AtomicOrderRelease || ordering == AtomicOrderAcqRel) {
|
|
ir_assert(instruction->ordering != nullptr, &instruction->base);
|
|
ir_add_error(ira, instruction->ordering,
|
|
buf_sprintf("@atomicLoad atomic ordering must not be Release or AcqRel"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (instr_is_comptime(casted_ptr)) {
|
|
IrInstruction *result = ir_get_deref(ira, &instruction->base, casted_ptr, nullptr);
|
|
ir_assert(result->value.type != nullptr, &instruction->base);
|
|
return result;
|
|
}
|
|
|
|
IrInstruction *result = ir_build_atomic_load(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node, nullptr, casted_ptr, nullptr, ordering);
|
|
result->value.type = operand_type;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_save_err_ret_addr(IrAnalyze *ira, IrInstructionSaveErrRetAddr *instruction) {
|
|
IrInstruction *result = ir_build_save_err_ret_addr(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node);
|
|
result->value.type = ira->codegen->builtin_types.entry_void;
|
|
return result;
|
|
}
|
|
|
|
static void ir_eval_float_op(IrAnalyze *ira, IrInstructionFloatOp *source_instr, ZigType *float_type,
|
|
ConstExprValue *op, ConstExprValue *out_val) {
|
|
assert(ira && source_instr && float_type && out_val && op);
|
|
assert(float_type->id == ZigTypeIdFloat ||
|
|
float_type->id == ZigTypeIdComptimeFloat);
|
|
|
|
BuiltinFnId fop = source_instr->op;
|
|
unsigned bits;
|
|
|
|
switch (float_type->id) {
|
|
case ZigTypeIdComptimeFloat:
|
|
bits = 128;
|
|
break;
|
|
case ZigTypeIdFloat:
|
|
bits = float_type->data.floating.bit_count;
|
|
break;
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
|
|
switch (bits) {
|
|
case 16: {
|
|
switch (fop) {
|
|
case BuiltinFnIdSqrt:
|
|
out_val->data.x_f16 = f16_sqrt(op->data.x_f16);
|
|
break;
|
|
case BuiltinFnIdSin:
|
|
case BuiltinFnIdCos:
|
|
case BuiltinFnIdExp:
|
|
case BuiltinFnIdExp2:
|
|
case BuiltinFnIdLn:
|
|
case BuiltinFnIdLog10:
|
|
case BuiltinFnIdLog2:
|
|
case BuiltinFnIdFabs:
|
|
case BuiltinFnIdFloor:
|
|
case BuiltinFnIdCeil:
|
|
case BuiltinFnIdTrunc:
|
|
case BuiltinFnIdNearbyInt:
|
|
case BuiltinFnIdRound:
|
|
zig_panic("unimplemented f16 builtin");
|
|
default:
|
|
zig_unreachable();
|
|
};
|
|
break;
|
|
};
|
|
case 32: {
|
|
switch (fop) {
|
|
case BuiltinFnIdSqrt:
|
|
out_val->data.x_f32 = sqrtf(op->data.x_f32);
|
|
break;
|
|
case BuiltinFnIdSin:
|
|
out_val->data.x_f32 = sinf(op->data.x_f32);
|
|
break;
|
|
case BuiltinFnIdCos:
|
|
out_val->data.x_f32 = cosf(op->data.x_f32);
|
|
break;
|
|
case BuiltinFnIdExp:
|
|
out_val->data.x_f32 = expf(op->data.x_f32);
|
|
break;
|
|
case BuiltinFnIdExp2:
|
|
out_val->data.x_f32 = exp2f(op->data.x_f32);
|
|
break;
|
|
case BuiltinFnIdLn:
|
|
out_val->data.x_f32 = logf(op->data.x_f32);
|
|
break;
|
|
case BuiltinFnIdLog10:
|
|
out_val->data.x_f32 = log10f(op->data.x_f32);
|
|
break;
|
|
case BuiltinFnIdLog2:
|
|
out_val->data.x_f32 = log2f(op->data.x_f32);
|
|
break;
|
|
case BuiltinFnIdFabs:
|
|
out_val->data.x_f32 = fabsf(op->data.x_f32);
|
|
break;
|
|
case BuiltinFnIdFloor:
|
|
out_val->data.x_f32 = floorf(op->data.x_f32);
|
|
break;
|
|
case BuiltinFnIdCeil:
|
|
out_val->data.x_f32 = ceilf(op->data.x_f32);
|
|
break;
|
|
case BuiltinFnIdTrunc:
|
|
out_val->data.x_f32 = truncf(op->data.x_f32);
|
|
break;
|
|
case BuiltinFnIdNearbyInt:
|
|
out_val->data.x_f32 = nearbyintf(op->data.x_f32);
|
|
break;
|
|
case BuiltinFnIdRound:
|
|
out_val->data.x_f32 = roundf(op->data.x_f32);
|
|
break;
|
|
default:
|
|
zig_unreachable();
|
|
};
|
|
break;
|
|
};
|
|
case 64: {
|
|
switch (fop) {
|
|
case BuiltinFnIdSqrt:
|
|
out_val->data.x_f64 = sqrt(op->data.x_f64);
|
|
break;
|
|
case BuiltinFnIdSin:
|
|
out_val->data.x_f64 = sin(op->data.x_f64);
|
|
break;
|
|
case BuiltinFnIdCos:
|
|
out_val->data.x_f64 = cos(op->data.x_f64);
|
|
break;
|
|
case BuiltinFnIdExp:
|
|
out_val->data.x_f64 = exp(op->data.x_f64);
|
|
break;
|
|
case BuiltinFnIdExp2:
|
|
out_val->data.x_f64 = exp2(op->data.x_f64);
|
|
break;
|
|
case BuiltinFnIdLn:
|
|
out_val->data.x_f64 = log(op->data.x_f64);
|
|
break;
|
|
case BuiltinFnIdLog10:
|
|
out_val->data.x_f64 = log10(op->data.x_f64);
|
|
break;
|
|
case BuiltinFnIdLog2:
|
|
out_val->data.x_f64 = log2(op->data.x_f64);
|
|
break;
|
|
case BuiltinFnIdFabs:
|
|
out_val->data.x_f64 = fabs(op->data.x_f64);
|
|
break;
|
|
case BuiltinFnIdFloor:
|
|
out_val->data.x_f64 = floor(op->data.x_f64);
|
|
break;
|
|
case BuiltinFnIdCeil:
|
|
out_val->data.x_f64 = ceil(op->data.x_f64);
|
|
break;
|
|
case BuiltinFnIdTrunc:
|
|
out_val->data.x_f64 = trunc(op->data.x_f64);
|
|
break;
|
|
case BuiltinFnIdNearbyInt:
|
|
out_val->data.x_f64 = nearbyint(op->data.x_f64);
|
|
break;
|
|
case BuiltinFnIdRound:
|
|
out_val->data.x_f64 = round(op->data.x_f64);
|
|
break;
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
break;
|
|
};
|
|
case 128: {
|
|
float128_t *out, *in;
|
|
if (float_type->id == ZigTypeIdComptimeFloat) {
|
|
out = &out_val->data.x_bigfloat.value;
|
|
in = &op->data.x_bigfloat.value;
|
|
} else {
|
|
out = &out_val->data.x_f128;
|
|
in = &op->data.x_f128;
|
|
}
|
|
switch (fop) {
|
|
case BuiltinFnIdSqrt:
|
|
f128M_sqrt(in, out);
|
|
break;
|
|
case BuiltinFnIdNearbyInt:
|
|
case BuiltinFnIdSin:
|
|
case BuiltinFnIdCos:
|
|
case BuiltinFnIdExp:
|
|
case BuiltinFnIdExp2:
|
|
case BuiltinFnIdLn:
|
|
case BuiltinFnIdLog10:
|
|
case BuiltinFnIdLog2:
|
|
case BuiltinFnIdFabs:
|
|
case BuiltinFnIdFloor:
|
|
case BuiltinFnIdCeil:
|
|
case BuiltinFnIdTrunc:
|
|
case BuiltinFnIdRound:
|
|
zig_panic("unimplemented f128 builtin");
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
break;
|
|
};
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_float_op(IrAnalyze *ira, IrInstructionFloatOp *instruction) {
|
|
IrInstruction *type = instruction->type->child;
|
|
if (type_is_invalid(type->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *expr_type = ir_resolve_type(ira, type);
|
|
if (type_is_invalid(expr_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
// Only allow float types, and vectors of floats.
|
|
ZigType *float_type = (expr_type->id == ZigTypeIdVector) ? expr_type->data.vector.elem_type : expr_type;
|
|
if (float_type->id != ZigTypeIdFloat && float_type->id != ZigTypeIdComptimeFloat) {
|
|
ir_add_error(ira, instruction->type, buf_sprintf("@%s does not support type '%s'", float_op_to_name(instruction->op, false), buf_ptr(&float_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *op1 = instruction->op1->child;
|
|
if (type_is_invalid(op1->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, float_type);
|
|
if (type_is_invalid(casted_op1->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (instr_is_comptime(casted_op1)) {
|
|
// Our comptime 16-bit and 128-bit support is quite limited.
|
|
if ((float_type->id == ZigTypeIdComptimeFloat ||
|
|
float_type->data.floating.bit_count == 16 ||
|
|
float_type->data.floating.bit_count == 128) &&
|
|
instruction->op != BuiltinFnIdSqrt) {
|
|
ir_add_error(ira, instruction->type, buf_sprintf("@%s does not support type '%s'", float_op_to_name(instruction->op, false), buf_ptr(&float_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
ConstExprValue *op1_const = ir_resolve_const(ira, casted_op1, UndefBad);
|
|
if (!op1_const)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *result = ir_const(ira, &instruction->base, expr_type);
|
|
ConstExprValue *out_val = &result->value;
|
|
|
|
if (expr_type->id == ZigTypeIdVector) {
|
|
expand_undef_array(ira->codegen, op1_const);
|
|
out_val->special = ConstValSpecialUndef;
|
|
expand_undef_array(ira->codegen, out_val);
|
|
size_t len = expr_type->data.vector.len;
|
|
for (size_t i = 0; i < len; i += 1) {
|
|
ConstExprValue *float_operand_op1 = &op1_const->data.x_array.data.s_none.elements[i];
|
|
ConstExprValue *float_out_val = &out_val->data.x_array.data.s_none.elements[i];
|
|
assert(float_operand_op1->type == float_type);
|
|
assert(float_out_val->type == float_type);
|
|
ir_eval_float_op(ira, instruction, float_type,
|
|
op1_const, float_out_val);
|
|
float_out_val->type = float_type;
|
|
}
|
|
out_val->type = expr_type;
|
|
out_val->special = ConstValSpecialStatic;
|
|
} else {
|
|
ir_eval_float_op(ira, instruction, float_type, op1_const, out_val);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
ir_assert(float_type->id == ZigTypeIdFloat, &instruction->base);
|
|
|
|
IrInstruction *result = ir_build_float_op(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node, nullptr, casted_op1, instruction->op);
|
|
result->value.type = expr_type;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_bswap(IrAnalyze *ira, IrInstructionBswap *instruction) {
|
|
ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child);
|
|
if (type_is_invalid(int_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *op = ir_implicit_cast(ira, instruction->op->child, int_type);
|
|
if (type_is_invalid(op->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (int_type->data.integral.bit_count == 0) {
|
|
IrInstruction *result = ir_const(ira, &instruction->base, int_type);
|
|
bigint_init_unsigned(&result->value.data.x_bigint, 0);
|
|
return result;
|
|
}
|
|
|
|
if (int_type->data.integral.bit_count == 8)
|
|
return op;
|
|
|
|
if (int_type->data.integral.bit_count % 8 != 0) {
|
|
ir_add_error(ira, instruction->op,
|
|
buf_sprintf("@byteSwap integer type '%s' has %" PRIu32 " bits which is not evenly divisible by 8",
|
|
buf_ptr(&int_type->name), int_type->data.integral.bit_count));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (instr_is_comptime(op)) {
|
|
ConstExprValue *val = ir_resolve_const(ira, op, UndefOk);
|
|
if (val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
if (val->special == ConstValSpecialUndef)
|
|
return ir_const_undef(ira, &instruction->base, int_type);
|
|
|
|
IrInstruction *result = ir_const(ira, &instruction->base, int_type);
|
|
size_t buf_size = int_type->data.integral.bit_count / 8;
|
|
uint8_t *buf = allocate_nonzero<uint8_t>(buf_size);
|
|
bigint_write_twos_complement(&val->data.x_bigint, buf, int_type->data.integral.bit_count, true);
|
|
bigint_read_twos_complement(&result->value.data.x_bigint, buf, int_type->data.integral.bit_count, false,
|
|
int_type->data.integral.is_signed);
|
|
return result;
|
|
}
|
|
|
|
IrInstruction *result = ir_build_bswap(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node, nullptr, op);
|
|
result->value.type = int_type;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_bit_reverse(IrAnalyze *ira, IrInstructionBitReverse *instruction) {
|
|
ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child);
|
|
if (type_is_invalid(int_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *op = ir_implicit_cast(ira, instruction->op->child, int_type);
|
|
if (type_is_invalid(op->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (int_type->data.integral.bit_count == 0) {
|
|
IrInstruction *result = ir_const(ira, &instruction->base, int_type);
|
|
bigint_init_unsigned(&result->value.data.x_bigint, 0);
|
|
return result;
|
|
}
|
|
|
|
if (instr_is_comptime(op)) {
|
|
ConstExprValue *val = ir_resolve_const(ira, op, UndefOk);
|
|
if (val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
if (val->special == ConstValSpecialUndef)
|
|
return ir_const_undef(ira, &instruction->base, int_type);
|
|
|
|
IrInstruction *result = ir_const(ira, &instruction->base, int_type);
|
|
size_t num_bits = int_type->data.integral.bit_count;
|
|
size_t buf_size = (num_bits + 7) / 8;
|
|
uint8_t *comptime_buf = allocate_nonzero<uint8_t>(buf_size);
|
|
uint8_t *result_buf = allocate_nonzero<uint8_t>(buf_size);
|
|
memset(comptime_buf,0,buf_size);
|
|
memset(result_buf,0,buf_size);
|
|
|
|
bigint_write_twos_complement(&val->data.x_bigint,comptime_buf,num_bits,ira->codegen->is_big_endian);
|
|
|
|
size_t bit_i = 0;
|
|
size_t bit_rev_i = num_bits - 1;
|
|
for (; bit_i < num_bits; bit_i++, bit_rev_i--) {
|
|
if (comptime_buf[bit_i / 8] & (1 << (bit_i % 8))) {
|
|
result_buf[bit_rev_i / 8] |= (1 << (bit_rev_i % 8));
|
|
}
|
|
}
|
|
|
|
bigint_read_twos_complement(&result->value.data.x_bigint,
|
|
result_buf,
|
|
int_type->data.integral.bit_count,
|
|
ira->codegen->is_big_endian,
|
|
int_type->data.integral.is_signed);
|
|
|
|
return result;
|
|
}
|
|
|
|
IrInstruction *result = ir_build_bit_reverse(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node, nullptr, op);
|
|
result->value.type = int_type;
|
|
return result;
|
|
}
|
|
|
|
|
|
static IrInstruction *ir_analyze_instruction_enum_to_int(IrAnalyze *ira, IrInstructionEnumToInt *instruction) {
|
|
IrInstruction *target = instruction->target->child;
|
|
if (type_is_invalid(target->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
return ir_analyze_enum_to_int(ira, &instruction->base, target);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_int_to_enum(IrAnalyze *ira, IrInstructionIntToEnum *instruction) {
|
|
Error err;
|
|
IrInstruction *dest_type_value = instruction->dest_type->child;
|
|
ZigType *dest_type = ir_resolve_type(ira, dest_type_value);
|
|
if (type_is_invalid(dest_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (dest_type->id != ZigTypeIdEnum) {
|
|
ir_add_error(ira, instruction->dest_type,
|
|
buf_sprintf("expected enum, found type '%s'", buf_ptr(&dest_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *tag_type = dest_type->data.enumeration.tag_int_type;
|
|
|
|
IrInstruction *target = instruction->target->child;
|
|
if (type_is_invalid(target->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *casted_target = ir_implicit_cast(ira, target, tag_type);
|
|
if (type_is_invalid(casted_target->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
return ir_analyze_int_to_enum(ira, &instruction->base, casted_target, dest_type);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_check_runtime_scope(IrAnalyze *ira, IrInstructionCheckRuntimeScope *instruction) {
|
|
IrInstruction *block_comptime_inst = instruction->scope_is_comptime->child;
|
|
bool scope_is_comptime;
|
|
if (!ir_resolve_bool(ira, block_comptime_inst, &scope_is_comptime))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *is_comptime_inst = instruction->is_comptime->child;
|
|
bool is_comptime;
|
|
if (!ir_resolve_bool(ira, is_comptime_inst, &is_comptime))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (!scope_is_comptime && is_comptime) {
|
|
ErrorMsg *msg = ir_add_error(ira, &instruction->base,
|
|
buf_sprintf("comptime control flow inside runtime block"));
|
|
add_error_note(ira->codegen, msg, block_comptime_inst->source_node,
|
|
buf_sprintf("runtime block created here"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
return ir_const_void(ira, &instruction->base);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_has_decl(IrAnalyze *ira, IrInstructionHasDecl *instruction) {
|
|
ZigType *container_type = ir_resolve_type(ira, instruction->container->child);
|
|
if (type_is_invalid(container_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
Buf *name = ir_resolve_str(ira, instruction->name->child);
|
|
if (name == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (!is_container(container_type)) {
|
|
ir_add_error(ira, instruction->container,
|
|
buf_sprintf("expected struct, enum, or union; found '%s'", buf_ptr(&container_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
ScopeDecls *container_scope = get_container_scope(container_type);
|
|
Tld *tld = find_container_decl(ira->codegen, container_scope, name);
|
|
if (tld == nullptr)
|
|
return ir_const_bool(ira, &instruction->base, false);
|
|
|
|
if (tld->visib_mod == VisibModPrivate && tld->import != get_scope_import(instruction->base.scope)) {
|
|
return ir_const_bool(ira, &instruction->base, false);
|
|
}
|
|
|
|
return ir_const_bool(ira, &instruction->base, true);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_undeclared_ident(IrAnalyze *ira, IrInstructionUndeclaredIdent *instruction) {
|
|
// put a variable of same name with invalid type in global scope
|
|
// so that future references to this same name will find a variable with an invalid type
|
|
populate_invalid_variable_in_scope(ira->codegen, instruction->base.scope, instruction->base.source_node,
|
|
instruction->name);
|
|
ir_add_error(ira, &instruction->base,
|
|
buf_sprintf("use of undeclared identifier '%s'", buf_ptr(instruction->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_end_expr(IrAnalyze *ira, IrInstructionEndExpr *instruction) {
|
|
IrInstruction *value = instruction->value->child;
|
|
if (type_is_invalid(value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
bool was_written = instruction->result_loc->written;
|
|
IrInstruction *result_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc,
|
|
value->value.type, value, false, false, true);
|
|
if (result_loc != nullptr) {
|
|
if (type_is_invalid(result_loc->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
if (result_loc->value.type->id == ZigTypeIdUnreachable)
|
|
return result_loc;
|
|
|
|
if (!was_written) {
|
|
IrInstruction *store_ptr = ir_analyze_store_ptr(ira, &instruction->base, result_loc, value,
|
|
instruction->result_loc->allow_write_through_const);
|
|
if (type_is_invalid(store_ptr->value.type)) {
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
|
|
if (result_loc->value.data.x_ptr.mut == ConstPtrMutInfer) {
|
|
if (instr_is_comptime(value)) {
|
|
result_loc->value.data.x_ptr.mut = ConstPtrMutComptimeConst;
|
|
} else {
|
|
result_loc->value.special = ConstValSpecialRuntime;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ir_const_void(ira, &instruction->base);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_bit_cast_src(IrAnalyze *ira, IrInstructionBitCastSrc *instruction) {
|
|
IrInstruction *operand = instruction->operand->child;
|
|
if (type_is_invalid(operand->value.type))
|
|
return operand;
|
|
|
|
IrInstruction *result_loc = ir_resolve_result(ira, &instruction->base,
|
|
&instruction->result_loc_bit_cast->base, operand->value.type, operand, false, false, true);
|
|
if (result_loc != nullptr && (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc)))
|
|
return result_loc;
|
|
|
|
return instruction->result_loc_bit_cast->parent->gen_instruction;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_union_init_named_field(IrAnalyze *ira,
|
|
IrInstructionUnionInitNamedField *instruction)
|
|
{
|
|
ZigType *union_type = ir_resolve_type(ira, instruction->union_type->child);
|
|
if (type_is_invalid(union_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (union_type->id != ZigTypeIdUnion) {
|
|
ir_add_error(ira, instruction->union_type,
|
|
buf_sprintf("non-union type '%s' passed to @unionInit", buf_ptr(&union_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
Buf *field_name = ir_resolve_str(ira, instruction->field_name->child);
|
|
if (field_name == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *field_result_loc = instruction->field_result_loc->child;
|
|
if (type_is_invalid(field_result_loc->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *result_loc = instruction->result_loc->child;
|
|
if (type_is_invalid(result_loc->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
return ir_analyze_union_init(ira, &instruction->base, instruction->base.source_node,
|
|
union_type, field_name, field_result_loc, result_loc);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_suspend_begin(IrAnalyze *ira, IrInstructionSuspendBegin *instruction) {
|
|
IrInstructionSuspendBegin *result = ir_build_suspend_begin(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node);
|
|
return &result->base;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_suspend_finish(IrAnalyze *ira,
|
|
IrInstructionSuspendFinish *instruction)
|
|
{
|
|
IrInstruction *begin_base = instruction->begin->base.child;
|
|
if (type_is_invalid(begin_base->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
ir_assert(begin_base->id == IrInstructionIdSuspendBegin, &instruction->base);
|
|
IrInstructionSuspendBegin *begin = reinterpret_cast<IrInstructionSuspendBegin *>(begin_base);
|
|
|
|
ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec);
|
|
ir_assert(fn_entry != nullptr, &instruction->base);
|
|
|
|
if (fn_entry->inferred_async_node == nullptr) {
|
|
fn_entry->inferred_async_node = instruction->base.source_node;
|
|
}
|
|
|
|
return ir_build_suspend_finish(&ira->new_irb, instruction->base.scope, instruction->base.source_node, begin);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_await(IrAnalyze *ira, IrInstructionAwaitSrc *instruction) {
|
|
IrInstruction *frame_ptr = instruction->frame->child;
|
|
if (type_is_invalid(frame_ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *result_type;
|
|
IrInstruction *frame;
|
|
if (frame_ptr->value.type->id == ZigTypeIdPointer &&
|
|
frame_ptr->value.type->data.pointer.ptr_len == PtrLenSingle &&
|
|
frame_ptr->value.type->data.pointer.child_type->id == ZigTypeIdCoroFrame)
|
|
{
|
|
result_type = frame_ptr->value.type->data.pointer.child_type->data.frame.fn->type_entry->data.fn.fn_type_id.return_type;
|
|
frame = frame_ptr;
|
|
} else {
|
|
frame = ir_get_deref(ira, &instruction->base, frame_ptr, nullptr);
|
|
if (frame->value.type->id != ZigTypeIdAnyFrame ||
|
|
frame->value.type->data.any_frame.result_type == nullptr)
|
|
{
|
|
ir_add_error(ira, &instruction->base,
|
|
buf_sprintf("expected anyframe->T, found '%s'", buf_ptr(&frame->value.type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
result_type = frame->value.type->data.any_frame.result_type;
|
|
}
|
|
|
|
ZigType *any_frame_type = get_any_frame_type(ira->codegen, result_type);
|
|
IrInstruction *casted_frame = ir_implicit_cast(ira, frame, any_frame_type);
|
|
if (type_is_invalid(casted_frame->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec);
|
|
ir_assert(fn_entry != nullptr, &instruction->base);
|
|
|
|
if (fn_entry->inferred_async_node == nullptr) {
|
|
fn_entry->inferred_async_node = instruction->base.source_node;
|
|
}
|
|
|
|
if (type_can_fail(result_type)) {
|
|
fn_entry->calls_or_awaits_errorable_fn = true;
|
|
}
|
|
|
|
IrInstruction *result_loc;
|
|
if (type_has_bits(result_type)) {
|
|
result_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc,
|
|
result_type, nullptr, true, false, true);
|
|
if (result_loc != nullptr && (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc)))
|
|
return result_loc;
|
|
} else {
|
|
result_loc = nullptr;
|
|
}
|
|
|
|
IrInstruction *result = ir_build_await_gen(ira, &instruction->base, frame, result_type, result_loc);
|
|
return ir_finish_anal(ira, result);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_coro_resume(IrAnalyze *ira, IrInstructionCoroResume *instruction) {
|
|
IrInstruction *frame_ptr = instruction->frame->child;
|
|
if (type_is_invalid(frame_ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *frame;
|
|
if (frame_ptr->value.type->id == ZigTypeIdPointer &&
|
|
frame_ptr->value.type->data.pointer.ptr_len == PtrLenSingle &&
|
|
frame_ptr->value.type->data.pointer.child_type->id == ZigTypeIdCoroFrame)
|
|
{
|
|
frame = frame_ptr;
|
|
} else {
|
|
frame = ir_get_deref(ira, &instruction->base, frame_ptr, nullptr);
|
|
}
|
|
|
|
ZigType *any_frame_type = get_any_frame_type(ira->codegen, nullptr);
|
|
IrInstruction *casted_frame = ir_implicit_cast(ira, frame, any_frame_type);
|
|
if (type_is_invalid(casted_frame->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
return ir_build_coro_resume(&ira->new_irb, instruction->base.scope, instruction->base.source_node, casted_frame);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction *instruction) {
|
|
switch (instruction->id) {
|
|
case IrInstructionIdInvalid:
|
|
case IrInstructionIdWidenOrShorten:
|
|
case IrInstructionIdStructFieldPtr:
|
|
case IrInstructionIdUnionFieldPtr:
|
|
case IrInstructionIdOptionalWrap:
|
|
case IrInstructionIdErrWrapCode:
|
|
case IrInstructionIdErrWrapPayload:
|
|
case IrInstructionIdCast:
|
|
case IrInstructionIdDeclVarGen:
|
|
case IrInstructionIdPtrCastGen:
|
|
case IrInstructionIdCmpxchgGen:
|
|
case IrInstructionIdArrayToVector:
|
|
case IrInstructionIdVectorToArray:
|
|
case IrInstructionIdPtrOfArrayToSlice:
|
|
case IrInstructionIdAssertZero:
|
|
case IrInstructionIdAssertNonNull:
|
|
case IrInstructionIdResizeSlice:
|
|
case IrInstructionIdLoadPtrGen:
|
|
case IrInstructionIdBitCastGen:
|
|
case IrInstructionIdCallGen:
|
|
case IrInstructionIdReturnPtr:
|
|
case IrInstructionIdAllocaGen:
|
|
case IrInstructionIdSliceGen:
|
|
case IrInstructionIdRefGen:
|
|
case IrInstructionIdTestErrGen:
|
|
case IrInstructionIdFrameSizeGen:
|
|
case IrInstructionIdAwaitGen:
|
|
zig_unreachable();
|
|
|
|
case IrInstructionIdReturnBegin:
|
|
return ir_analyze_instruction_return_begin(ira, (IrInstructionReturnBegin *)instruction);
|
|
case IrInstructionIdReturn:
|
|
return ir_analyze_instruction_return(ira, (IrInstructionReturn *)instruction);
|
|
case IrInstructionIdConst:
|
|
return ir_analyze_instruction_const(ira, (IrInstructionConst *)instruction);
|
|
case IrInstructionIdUnOp:
|
|
return ir_analyze_instruction_un_op(ira, (IrInstructionUnOp *)instruction);
|
|
case IrInstructionIdBinOp:
|
|
return ir_analyze_instruction_bin_op(ira, (IrInstructionBinOp *)instruction);
|
|
case IrInstructionIdDeclVarSrc:
|
|
return ir_analyze_instruction_decl_var(ira, (IrInstructionDeclVarSrc *)instruction);
|
|
case IrInstructionIdLoadPtr:
|
|
return ir_analyze_instruction_load_ptr(ira, (IrInstructionLoadPtr *)instruction);
|
|
case IrInstructionIdStorePtr:
|
|
return ir_analyze_instruction_store_ptr(ira, (IrInstructionStorePtr *)instruction);
|
|
case IrInstructionIdElemPtr:
|
|
return ir_analyze_instruction_elem_ptr(ira, (IrInstructionElemPtr *)instruction);
|
|
case IrInstructionIdVarPtr:
|
|
return ir_analyze_instruction_var_ptr(ira, (IrInstructionVarPtr *)instruction);
|
|
case IrInstructionIdFieldPtr:
|
|
return ir_analyze_instruction_field_ptr(ira, (IrInstructionFieldPtr *)instruction);
|
|
case IrInstructionIdCallSrc:
|
|
return ir_analyze_instruction_call(ira, (IrInstructionCallSrc *)instruction);
|
|
case IrInstructionIdBr:
|
|
return ir_analyze_instruction_br(ira, (IrInstructionBr *)instruction);
|
|
case IrInstructionIdCondBr:
|
|
return ir_analyze_instruction_cond_br(ira, (IrInstructionCondBr *)instruction);
|
|
case IrInstructionIdUnreachable:
|
|
return ir_analyze_instruction_unreachable(ira, (IrInstructionUnreachable *)instruction);
|
|
case IrInstructionIdPhi:
|
|
return ir_analyze_instruction_phi(ira, (IrInstructionPhi *)instruction);
|
|
case IrInstructionIdTypeOf:
|
|
return ir_analyze_instruction_typeof(ira, (IrInstructionTypeOf *)instruction);
|
|
case IrInstructionIdSetCold:
|
|
return ir_analyze_instruction_set_cold(ira, (IrInstructionSetCold *)instruction);
|
|
case IrInstructionIdSetRuntimeSafety:
|
|
return ir_analyze_instruction_set_runtime_safety(ira, (IrInstructionSetRuntimeSafety *)instruction);
|
|
case IrInstructionIdSetFloatMode:
|
|
return ir_analyze_instruction_set_float_mode(ira, (IrInstructionSetFloatMode *)instruction);
|
|
case IrInstructionIdAnyFrameType:
|
|
return ir_analyze_instruction_any_frame_type(ira, (IrInstructionAnyFrameType *)instruction);
|
|
case IrInstructionIdSliceType:
|
|
return ir_analyze_instruction_slice_type(ira, (IrInstructionSliceType *)instruction);
|
|
case IrInstructionIdGlobalAsm:
|
|
return ir_analyze_instruction_global_asm(ira, (IrInstructionGlobalAsm *)instruction);
|
|
case IrInstructionIdAsm:
|
|
return ir_analyze_instruction_asm(ira, (IrInstructionAsm *)instruction);
|
|
case IrInstructionIdArrayType:
|
|
return ir_analyze_instruction_array_type(ira, (IrInstructionArrayType *)instruction);
|
|
case IrInstructionIdSizeOf:
|
|
return ir_analyze_instruction_size_of(ira, (IrInstructionSizeOf *)instruction);
|
|
case IrInstructionIdTestNonNull:
|
|
return ir_analyze_instruction_test_non_null(ira, (IrInstructionTestNonNull *)instruction);
|
|
case IrInstructionIdOptionalUnwrapPtr:
|
|
return ir_analyze_instruction_optional_unwrap_ptr(ira, (IrInstructionOptionalUnwrapPtr *)instruction);
|
|
case IrInstructionIdClz:
|
|
return ir_analyze_instruction_clz(ira, (IrInstructionClz *)instruction);
|
|
case IrInstructionIdCtz:
|
|
return ir_analyze_instruction_ctz(ira, (IrInstructionCtz *)instruction);
|
|
case IrInstructionIdPopCount:
|
|
return ir_analyze_instruction_pop_count(ira, (IrInstructionPopCount *)instruction);
|
|
case IrInstructionIdBswap:
|
|
return ir_analyze_instruction_bswap(ira, (IrInstructionBswap *)instruction);
|
|
case IrInstructionIdBitReverse:
|
|
return ir_analyze_instruction_bit_reverse(ira, (IrInstructionBitReverse *)instruction);
|
|
case IrInstructionIdSwitchBr:
|
|
return ir_analyze_instruction_switch_br(ira, (IrInstructionSwitchBr *)instruction);
|
|
case IrInstructionIdSwitchTarget:
|
|
return ir_analyze_instruction_switch_target(ira, (IrInstructionSwitchTarget *)instruction);
|
|
case IrInstructionIdSwitchVar:
|
|
return ir_analyze_instruction_switch_var(ira, (IrInstructionSwitchVar *)instruction);
|
|
case IrInstructionIdSwitchElseVar:
|
|
return ir_analyze_instruction_switch_else_var(ira, (IrInstructionSwitchElseVar *)instruction);
|
|
case IrInstructionIdUnionTag:
|
|
return ir_analyze_instruction_union_tag(ira, (IrInstructionUnionTag *)instruction);
|
|
case IrInstructionIdImport:
|
|
return ir_analyze_instruction_import(ira, (IrInstructionImport *)instruction);
|
|
case IrInstructionIdRef:
|
|
return ir_analyze_instruction_ref(ira, (IrInstructionRef *)instruction);
|
|
case IrInstructionIdContainerInitList:
|
|
return ir_analyze_instruction_container_init_list(ira, (IrInstructionContainerInitList *)instruction);
|
|
case IrInstructionIdContainerInitFields:
|
|
return ir_analyze_instruction_container_init_fields(ira, (IrInstructionContainerInitFields *)instruction);
|
|
case IrInstructionIdCompileErr:
|
|
return ir_analyze_instruction_compile_err(ira, (IrInstructionCompileErr *)instruction);
|
|
case IrInstructionIdCompileLog:
|
|
return ir_analyze_instruction_compile_log(ira, (IrInstructionCompileLog *)instruction);
|
|
case IrInstructionIdErrName:
|
|
return ir_analyze_instruction_err_name(ira, (IrInstructionErrName *)instruction);
|
|
case IrInstructionIdTypeName:
|
|
return ir_analyze_instruction_type_name(ira, (IrInstructionTypeName *)instruction);
|
|
case IrInstructionIdCImport:
|
|
return ir_analyze_instruction_c_import(ira, (IrInstructionCImport *)instruction);
|
|
case IrInstructionIdCInclude:
|
|
return ir_analyze_instruction_c_include(ira, (IrInstructionCInclude *)instruction);
|
|
case IrInstructionIdCDefine:
|
|
return ir_analyze_instruction_c_define(ira, (IrInstructionCDefine *)instruction);
|
|
case IrInstructionIdCUndef:
|
|
return ir_analyze_instruction_c_undef(ira, (IrInstructionCUndef *)instruction);
|
|
case IrInstructionIdEmbedFile:
|
|
return ir_analyze_instruction_embed_file(ira, (IrInstructionEmbedFile *)instruction);
|
|
case IrInstructionIdCmpxchgSrc:
|
|
return ir_analyze_instruction_cmpxchg(ira, (IrInstructionCmpxchgSrc *)instruction);
|
|
case IrInstructionIdFence:
|
|
return ir_analyze_instruction_fence(ira, (IrInstructionFence *)instruction);
|
|
case IrInstructionIdTruncate:
|
|
return ir_analyze_instruction_truncate(ira, (IrInstructionTruncate *)instruction);
|
|
case IrInstructionIdIntCast:
|
|
return ir_analyze_instruction_int_cast(ira, (IrInstructionIntCast *)instruction);
|
|
case IrInstructionIdFloatCast:
|
|
return ir_analyze_instruction_float_cast(ira, (IrInstructionFloatCast *)instruction);
|
|
case IrInstructionIdErrSetCast:
|
|
return ir_analyze_instruction_err_set_cast(ira, (IrInstructionErrSetCast *)instruction);
|
|
case IrInstructionIdFromBytes:
|
|
return ir_analyze_instruction_from_bytes(ira, (IrInstructionFromBytes *)instruction);
|
|
case IrInstructionIdToBytes:
|
|
return ir_analyze_instruction_to_bytes(ira, (IrInstructionToBytes *)instruction);
|
|
case IrInstructionIdIntToFloat:
|
|
return ir_analyze_instruction_int_to_float(ira, (IrInstructionIntToFloat *)instruction);
|
|
case IrInstructionIdFloatToInt:
|
|
return ir_analyze_instruction_float_to_int(ira, (IrInstructionFloatToInt *)instruction);
|
|
case IrInstructionIdBoolToInt:
|
|
return ir_analyze_instruction_bool_to_int(ira, (IrInstructionBoolToInt *)instruction);
|
|
case IrInstructionIdIntType:
|
|
return ir_analyze_instruction_int_type(ira, (IrInstructionIntType *)instruction);
|
|
case IrInstructionIdVectorType:
|
|
return ir_analyze_instruction_vector_type(ira, (IrInstructionVectorType *)instruction);
|
|
case IrInstructionIdBoolNot:
|
|
return ir_analyze_instruction_bool_not(ira, (IrInstructionBoolNot *)instruction);
|
|
case IrInstructionIdMemset:
|
|
return ir_analyze_instruction_memset(ira, (IrInstructionMemset *)instruction);
|
|
case IrInstructionIdMemcpy:
|
|
return ir_analyze_instruction_memcpy(ira, (IrInstructionMemcpy *)instruction);
|
|
case IrInstructionIdSliceSrc:
|
|
return ir_analyze_instruction_slice(ira, (IrInstructionSliceSrc *)instruction);
|
|
case IrInstructionIdMemberCount:
|
|
return ir_analyze_instruction_member_count(ira, (IrInstructionMemberCount *)instruction);
|
|
case IrInstructionIdMemberType:
|
|
return ir_analyze_instruction_member_type(ira, (IrInstructionMemberType *)instruction);
|
|
case IrInstructionIdMemberName:
|
|
return ir_analyze_instruction_member_name(ira, (IrInstructionMemberName *)instruction);
|
|
case IrInstructionIdBreakpoint:
|
|
return ir_analyze_instruction_breakpoint(ira, (IrInstructionBreakpoint *)instruction);
|
|
case IrInstructionIdReturnAddress:
|
|
return ir_analyze_instruction_return_address(ira, (IrInstructionReturnAddress *)instruction);
|
|
case IrInstructionIdFrameAddress:
|
|
return ir_analyze_instruction_frame_address(ira, (IrInstructionFrameAddress *)instruction);
|
|
case IrInstructionIdFrameHandle:
|
|
return ir_analyze_instruction_frame_handle(ira, (IrInstructionFrameHandle *)instruction);
|
|
case IrInstructionIdFrameType:
|
|
return ir_analyze_instruction_frame_type(ira, (IrInstructionFrameType *)instruction);
|
|
case IrInstructionIdFrameSizeSrc:
|
|
return ir_analyze_instruction_frame_size(ira, (IrInstructionFrameSizeSrc *)instruction);
|
|
case IrInstructionIdAlignOf:
|
|
return ir_analyze_instruction_align_of(ira, (IrInstructionAlignOf *)instruction);
|
|
case IrInstructionIdOverflowOp:
|
|
return ir_analyze_instruction_overflow_op(ira, (IrInstructionOverflowOp *)instruction);
|
|
case IrInstructionIdTestErrSrc:
|
|
return ir_analyze_instruction_test_err(ira, (IrInstructionTestErrSrc *)instruction);
|
|
case IrInstructionIdUnwrapErrCode:
|
|
return ir_analyze_instruction_unwrap_err_code(ira, (IrInstructionUnwrapErrCode *)instruction);
|
|
case IrInstructionIdUnwrapErrPayload:
|
|
return ir_analyze_instruction_unwrap_err_payload(ira, (IrInstructionUnwrapErrPayload *)instruction);
|
|
case IrInstructionIdFnProto:
|
|
return ir_analyze_instruction_fn_proto(ira, (IrInstructionFnProto *)instruction);
|
|
case IrInstructionIdTestComptime:
|
|
return ir_analyze_instruction_test_comptime(ira, (IrInstructionTestComptime *)instruction);
|
|
case IrInstructionIdCheckSwitchProngs:
|
|
return ir_analyze_instruction_check_switch_prongs(ira, (IrInstructionCheckSwitchProngs *)instruction);
|
|
case IrInstructionIdCheckStatementIsVoid:
|
|
return ir_analyze_instruction_check_statement_is_void(ira, (IrInstructionCheckStatementIsVoid *)instruction);
|
|
case IrInstructionIdDeclRef:
|
|
return ir_analyze_instruction_decl_ref(ira, (IrInstructionDeclRef *)instruction);
|
|
case IrInstructionIdPanic:
|
|
return ir_analyze_instruction_panic(ira, (IrInstructionPanic *)instruction);
|
|
case IrInstructionIdPtrCastSrc:
|
|
return ir_analyze_instruction_ptr_cast(ira, (IrInstructionPtrCastSrc *)instruction);
|
|
case IrInstructionIdIntToPtr:
|
|
return ir_analyze_instruction_int_to_ptr(ira, (IrInstructionIntToPtr *)instruction);
|
|
case IrInstructionIdPtrToInt:
|
|
return ir_analyze_instruction_ptr_to_int(ira, (IrInstructionPtrToInt *)instruction);
|
|
case IrInstructionIdTagName:
|
|
return ir_analyze_instruction_enum_tag_name(ira, (IrInstructionTagName *)instruction);
|
|
case IrInstructionIdFieldParentPtr:
|
|
return ir_analyze_instruction_field_parent_ptr(ira, (IrInstructionFieldParentPtr *)instruction);
|
|
case IrInstructionIdByteOffsetOf:
|
|
return ir_analyze_instruction_byte_offset_of(ira, (IrInstructionByteOffsetOf *)instruction);
|
|
case IrInstructionIdBitOffsetOf:
|
|
return ir_analyze_instruction_bit_offset_of(ira, (IrInstructionBitOffsetOf *)instruction);
|
|
case IrInstructionIdTypeInfo:
|
|
return ir_analyze_instruction_type_info(ira, (IrInstructionTypeInfo *) instruction);
|
|
case IrInstructionIdHasField:
|
|
return ir_analyze_instruction_has_field(ira, (IrInstructionHasField *) instruction);
|
|
case IrInstructionIdTypeId:
|
|
return ir_analyze_instruction_type_id(ira, (IrInstructionTypeId *)instruction);
|
|
case IrInstructionIdSetEvalBranchQuota:
|
|
return ir_analyze_instruction_set_eval_branch_quota(ira, (IrInstructionSetEvalBranchQuota *)instruction);
|
|
case IrInstructionIdPtrType:
|
|
return ir_analyze_instruction_ptr_type(ira, (IrInstructionPtrType *)instruction);
|
|
case IrInstructionIdAlignCast:
|
|
return ir_analyze_instruction_align_cast(ira, (IrInstructionAlignCast *)instruction);
|
|
case IrInstructionIdImplicitCast:
|
|
return ir_analyze_instruction_implicit_cast(ira, (IrInstructionImplicitCast *)instruction);
|
|
case IrInstructionIdResolveResult:
|
|
return ir_analyze_instruction_resolve_result(ira, (IrInstructionResolveResult *)instruction);
|
|
case IrInstructionIdResetResult:
|
|
return ir_analyze_instruction_reset_result(ira, (IrInstructionResetResult *)instruction);
|
|
case IrInstructionIdOpaqueType:
|
|
return ir_analyze_instruction_opaque_type(ira, (IrInstructionOpaqueType *)instruction);
|
|
case IrInstructionIdSetAlignStack:
|
|
return ir_analyze_instruction_set_align_stack(ira, (IrInstructionSetAlignStack *)instruction);
|
|
case IrInstructionIdArgType:
|
|
return ir_analyze_instruction_arg_type(ira, (IrInstructionArgType *)instruction);
|
|
case IrInstructionIdTagType:
|
|
return ir_analyze_instruction_tag_type(ira, (IrInstructionTagType *)instruction);
|
|
case IrInstructionIdExport:
|
|
return ir_analyze_instruction_export(ira, (IrInstructionExport *)instruction);
|
|
case IrInstructionIdErrorReturnTrace:
|
|
return ir_analyze_instruction_error_return_trace(ira, (IrInstructionErrorReturnTrace *)instruction);
|
|
case IrInstructionIdErrorUnion:
|
|
return ir_analyze_instruction_error_union(ira, (IrInstructionErrorUnion *)instruction);
|
|
case IrInstructionIdCancel:
|
|
return ir_analyze_instruction_cancel(ira, (IrInstructionCancel *)instruction);
|
|
case IrInstructionIdAtomicRmw:
|
|
return ir_analyze_instruction_atomic_rmw(ira, (IrInstructionAtomicRmw *)instruction);
|
|
case IrInstructionIdAtomicLoad:
|
|
return ir_analyze_instruction_atomic_load(ira, (IrInstructionAtomicLoad *)instruction);
|
|
case IrInstructionIdSaveErrRetAddr:
|
|
return ir_analyze_instruction_save_err_ret_addr(ira, (IrInstructionSaveErrRetAddr *)instruction);
|
|
case IrInstructionIdAddImplicitReturnType:
|
|
return ir_analyze_instruction_add_implicit_return_type(ira, (IrInstructionAddImplicitReturnType *)instruction);
|
|
case IrInstructionIdFloatOp:
|
|
return ir_analyze_instruction_float_op(ira, (IrInstructionFloatOp *)instruction);
|
|
case IrInstructionIdMulAdd:
|
|
return ir_analyze_instruction_mul_add(ira, (IrInstructionMulAdd *)instruction);
|
|
case IrInstructionIdIntToErr:
|
|
return ir_analyze_instruction_int_to_err(ira, (IrInstructionIntToErr *)instruction);
|
|
case IrInstructionIdErrToInt:
|
|
return ir_analyze_instruction_err_to_int(ira, (IrInstructionErrToInt *)instruction);
|
|
case IrInstructionIdIntToEnum:
|
|
return ir_analyze_instruction_int_to_enum(ira, (IrInstructionIntToEnum *)instruction);
|
|
case IrInstructionIdEnumToInt:
|
|
return ir_analyze_instruction_enum_to_int(ira, (IrInstructionEnumToInt *)instruction);
|
|
case IrInstructionIdCheckRuntimeScope:
|
|
return ir_analyze_instruction_check_runtime_scope(ira, (IrInstructionCheckRuntimeScope *)instruction);
|
|
case IrInstructionIdHasDecl:
|
|
return ir_analyze_instruction_has_decl(ira, (IrInstructionHasDecl *)instruction);
|
|
case IrInstructionIdUndeclaredIdent:
|
|
return ir_analyze_instruction_undeclared_ident(ira, (IrInstructionUndeclaredIdent *)instruction);
|
|
case IrInstructionIdAllocaSrc:
|
|
return nullptr;
|
|
case IrInstructionIdEndExpr:
|
|
return ir_analyze_instruction_end_expr(ira, (IrInstructionEndExpr *)instruction);
|
|
case IrInstructionIdBitCastSrc:
|
|
return ir_analyze_instruction_bit_cast_src(ira, (IrInstructionBitCastSrc *)instruction);
|
|
case IrInstructionIdUnionInitNamedField:
|
|
return ir_analyze_instruction_union_init_named_field(ira, (IrInstructionUnionInitNamedField *)instruction);
|
|
case IrInstructionIdSuspendBegin:
|
|
return ir_analyze_instruction_suspend_begin(ira, (IrInstructionSuspendBegin *)instruction);
|
|
case IrInstructionIdSuspendFinish:
|
|
return ir_analyze_instruction_suspend_finish(ira, (IrInstructionSuspendFinish *)instruction);
|
|
case IrInstructionIdCoroResume:
|
|
return ir_analyze_instruction_coro_resume(ira, (IrInstructionCoroResume *)instruction);
|
|
case IrInstructionIdAwaitSrc:
|
|
return ir_analyze_instruction_await(ira, (IrInstructionAwaitSrc *)instruction);
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
// This function attempts to evaluate IR code while doing type checking and other analysis.
|
|
// It emits a new IrExecutable which is partially evaluated IR code.
|
|
ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_exec,
|
|
ZigType *expected_type, AstNode *expected_type_source_node)
|
|
{
|
|
assert(!old_exec->invalid);
|
|
assert(expected_type == nullptr || !type_is_invalid(expected_type));
|
|
|
|
IrAnalyze *ira = allocate<IrAnalyze>(1);
|
|
old_exec->analysis = ira;
|
|
ira->codegen = codegen;
|
|
|
|
ira->explicit_return_type = expected_type;
|
|
ira->explicit_return_type_source_node = expected_type_source_node;
|
|
|
|
ira->old_irb.codegen = codegen;
|
|
ira->old_irb.exec = old_exec;
|
|
|
|
ira->new_irb.codegen = codegen;
|
|
ira->new_irb.exec = new_exec;
|
|
|
|
ConstExprValue *vals = create_const_vals(ira->old_irb.exec->mem_slot_count);
|
|
ira->exec_context.mem_slot_list.resize(ira->old_irb.exec->mem_slot_count);
|
|
for (size_t i = 0; i < ira->exec_context.mem_slot_list.length; i += 1) {
|
|
ira->exec_context.mem_slot_list.items[i] = &vals[i];
|
|
}
|
|
|
|
IrBasicBlock *old_entry_bb = ira->old_irb.exec->basic_block_list.at(0);
|
|
IrBasicBlock *new_entry_bb = ir_get_new_bb(ira, old_entry_bb, nullptr);
|
|
ir_ref_bb(new_entry_bb);
|
|
ira->new_irb.current_basic_block = new_entry_bb;
|
|
ira->old_bb_index = 0;
|
|
|
|
ir_start_bb(ira, old_entry_bb, nullptr);
|
|
|
|
while (ira->old_bb_index < ira->old_irb.exec->basic_block_list.length) {
|
|
IrInstruction *old_instruction = ira->old_irb.current_basic_block->instruction_list.at(ira->instruction_index);
|
|
|
|
if (old_instruction->ref_count == 0 && !ir_has_side_effects(old_instruction)) {
|
|
ira->instruction_index += 1;
|
|
continue;
|
|
}
|
|
|
|
if (ira->codegen->verbose_ir) {
|
|
fprintf(stderr, "analyze #%zu\n", old_instruction->debug_id);
|
|
}
|
|
IrInstruction *new_instruction = ir_analyze_instruction_base(ira, old_instruction);
|
|
if (new_instruction != nullptr) {
|
|
ir_assert(new_instruction->value.type != nullptr || new_instruction->value.type != nullptr, old_instruction);
|
|
old_instruction->child = new_instruction;
|
|
|
|
if (type_is_invalid(new_instruction->value.type)) {
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
}
|
|
|
|
// unreachable instructions do their own control flow.
|
|
if (new_instruction->value.type->id == ZigTypeIdUnreachable)
|
|
continue;
|
|
}
|
|
|
|
ira->instruction_index += 1;
|
|
}
|
|
|
|
if (new_exec->invalid) {
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
} else if (ira->src_implicit_return_type_list.length == 0) {
|
|
return codegen->builtin_types.entry_unreachable;
|
|
} else {
|
|
return ir_resolve_peer_types(ira, expected_type_source_node, expected_type, ira->src_implicit_return_type_list.items,
|
|
ira->src_implicit_return_type_list.length);
|
|
}
|
|
}
|
|
|
|
bool ir_has_side_effects(IrInstruction *instruction) {
|
|
switch (instruction->id) {
|
|
case IrInstructionIdInvalid:
|
|
zig_unreachable();
|
|
case IrInstructionIdBr:
|
|
case IrInstructionIdCondBr:
|
|
case IrInstructionIdSwitchBr:
|
|
case IrInstructionIdDeclVarSrc:
|
|
case IrInstructionIdDeclVarGen:
|
|
case IrInstructionIdStorePtr:
|
|
case IrInstructionIdCallSrc:
|
|
case IrInstructionIdCallGen:
|
|
case IrInstructionIdReturnBegin:
|
|
case IrInstructionIdReturn:
|
|
case IrInstructionIdUnreachable:
|
|
case IrInstructionIdSetCold:
|
|
case IrInstructionIdSetRuntimeSafety:
|
|
case IrInstructionIdSetFloatMode:
|
|
case IrInstructionIdImport:
|
|
case IrInstructionIdCompileErr:
|
|
case IrInstructionIdCompileLog:
|
|
case IrInstructionIdCImport:
|
|
case IrInstructionIdCInclude:
|
|
case IrInstructionIdCDefine:
|
|
case IrInstructionIdCUndef:
|
|
case IrInstructionIdFence:
|
|
case IrInstructionIdMemset:
|
|
case IrInstructionIdMemcpy:
|
|
case IrInstructionIdBreakpoint:
|
|
case IrInstructionIdOverflowOp: // TODO when we support multiple returns this can be side effect free
|
|
case IrInstructionIdCheckSwitchProngs:
|
|
case IrInstructionIdCheckStatementIsVoid:
|
|
case IrInstructionIdCheckRuntimeScope:
|
|
case IrInstructionIdPanic:
|
|
case IrInstructionIdSetEvalBranchQuota:
|
|
case IrInstructionIdPtrType:
|
|
case IrInstructionIdSetAlignStack:
|
|
case IrInstructionIdExport:
|
|
case IrInstructionIdCancel:
|
|
case IrInstructionIdSaveErrRetAddr:
|
|
case IrInstructionIdAddImplicitReturnType:
|
|
case IrInstructionIdAtomicRmw:
|
|
case IrInstructionIdCmpxchgGen:
|
|
case IrInstructionIdCmpxchgSrc:
|
|
case IrInstructionIdAssertZero:
|
|
case IrInstructionIdAssertNonNull:
|
|
case IrInstructionIdResizeSlice:
|
|
case IrInstructionIdGlobalAsm:
|
|
case IrInstructionIdUndeclaredIdent:
|
|
case IrInstructionIdEndExpr:
|
|
case IrInstructionIdPtrOfArrayToSlice:
|
|
case IrInstructionIdSliceGen:
|
|
case IrInstructionIdOptionalWrap:
|
|
case IrInstructionIdVectorToArray:
|
|
case IrInstructionIdResetResult:
|
|
case IrInstructionIdSuspendBegin:
|
|
case IrInstructionIdSuspendFinish:
|
|
case IrInstructionIdCoroResume:
|
|
case IrInstructionIdAwaitSrc:
|
|
case IrInstructionIdAwaitGen:
|
|
return true;
|
|
|
|
case IrInstructionIdPhi:
|
|
case IrInstructionIdUnOp:
|
|
case IrInstructionIdBinOp:
|
|
case IrInstructionIdLoadPtr:
|
|
case IrInstructionIdConst:
|
|
case IrInstructionIdCast:
|
|
case IrInstructionIdContainerInitList:
|
|
case IrInstructionIdContainerInitFields:
|
|
case IrInstructionIdUnionInitNamedField:
|
|
case IrInstructionIdFieldPtr:
|
|
case IrInstructionIdElemPtr:
|
|
case IrInstructionIdVarPtr:
|
|
case IrInstructionIdReturnPtr:
|
|
case IrInstructionIdTypeOf:
|
|
case IrInstructionIdStructFieldPtr:
|
|
case IrInstructionIdArrayType:
|
|
case IrInstructionIdSliceType:
|
|
case IrInstructionIdAnyFrameType:
|
|
case IrInstructionIdSizeOf:
|
|
case IrInstructionIdTestNonNull:
|
|
case IrInstructionIdOptionalUnwrapPtr:
|
|
case IrInstructionIdClz:
|
|
case IrInstructionIdCtz:
|
|
case IrInstructionIdPopCount:
|
|
case IrInstructionIdBswap:
|
|
case IrInstructionIdBitReverse:
|
|
case IrInstructionIdSwitchVar:
|
|
case IrInstructionIdSwitchElseVar:
|
|
case IrInstructionIdSwitchTarget:
|
|
case IrInstructionIdUnionTag:
|
|
case IrInstructionIdRef:
|
|
case IrInstructionIdEmbedFile:
|
|
case IrInstructionIdTruncate:
|
|
case IrInstructionIdIntType:
|
|
case IrInstructionIdVectorType:
|
|
case IrInstructionIdBoolNot:
|
|
case IrInstructionIdSliceSrc:
|
|
case IrInstructionIdMemberCount:
|
|
case IrInstructionIdMemberType:
|
|
case IrInstructionIdMemberName:
|
|
case IrInstructionIdAlignOf:
|
|
case IrInstructionIdReturnAddress:
|
|
case IrInstructionIdFrameAddress:
|
|
case IrInstructionIdFrameHandle:
|
|
case IrInstructionIdFrameType:
|
|
case IrInstructionIdFrameSizeSrc:
|
|
case IrInstructionIdFrameSizeGen:
|
|
case IrInstructionIdTestErrSrc:
|
|
case IrInstructionIdTestErrGen:
|
|
case IrInstructionIdFnProto:
|
|
case IrInstructionIdTestComptime:
|
|
case IrInstructionIdPtrCastSrc:
|
|
case IrInstructionIdPtrCastGen:
|
|
case IrInstructionIdBitCastSrc:
|
|
case IrInstructionIdBitCastGen:
|
|
case IrInstructionIdWidenOrShorten:
|
|
case IrInstructionIdPtrToInt:
|
|
case IrInstructionIdIntToPtr:
|
|
case IrInstructionIdIntToEnum:
|
|
case IrInstructionIdIntToErr:
|
|
case IrInstructionIdErrToInt:
|
|
case IrInstructionIdDeclRef:
|
|
case IrInstructionIdErrName:
|
|
case IrInstructionIdTypeName:
|
|
case IrInstructionIdTagName:
|
|
case IrInstructionIdFieldParentPtr:
|
|
case IrInstructionIdByteOffsetOf:
|
|
case IrInstructionIdBitOffsetOf:
|
|
case IrInstructionIdTypeInfo:
|
|
case IrInstructionIdHasField:
|
|
case IrInstructionIdTypeId:
|
|
case IrInstructionIdAlignCast:
|
|
case IrInstructionIdImplicitCast:
|
|
case IrInstructionIdResolveResult:
|
|
case IrInstructionIdOpaqueType:
|
|
case IrInstructionIdArgType:
|
|
case IrInstructionIdTagType:
|
|
case IrInstructionIdErrorReturnTrace:
|
|
case IrInstructionIdErrorUnion:
|
|
case IrInstructionIdFloatOp:
|
|
case IrInstructionIdMulAdd:
|
|
case IrInstructionIdAtomicLoad:
|
|
case IrInstructionIdIntCast:
|
|
case IrInstructionIdFloatCast:
|
|
case IrInstructionIdErrSetCast:
|
|
case IrInstructionIdIntToFloat:
|
|
case IrInstructionIdFloatToInt:
|
|
case IrInstructionIdBoolToInt:
|
|
case IrInstructionIdFromBytes:
|
|
case IrInstructionIdToBytes:
|
|
case IrInstructionIdEnumToInt:
|
|
case IrInstructionIdArrayToVector:
|
|
case IrInstructionIdHasDecl:
|
|
case IrInstructionIdAllocaSrc:
|
|
case IrInstructionIdAllocaGen:
|
|
return false;
|
|
|
|
case IrInstructionIdAsm:
|
|
{
|
|
IrInstructionAsm *asm_instruction = (IrInstructionAsm *)instruction;
|
|
return asm_instruction->has_side_effects;
|
|
}
|
|
case IrInstructionIdUnwrapErrPayload:
|
|
{
|
|
IrInstructionUnwrapErrPayload *unwrap_err_payload_instruction =
|
|
(IrInstructionUnwrapErrPayload *)instruction;
|
|
return unwrap_err_payload_instruction->safety_check_on ||
|
|
unwrap_err_payload_instruction->initializing;
|
|
}
|
|
case IrInstructionIdUnwrapErrCode:
|
|
return reinterpret_cast<IrInstructionUnwrapErrCode *>(instruction)->initializing;
|
|
case IrInstructionIdUnionFieldPtr:
|
|
return reinterpret_cast<IrInstructionUnionFieldPtr *>(instruction)->initializing;
|
|
case IrInstructionIdErrWrapPayload:
|
|
return reinterpret_cast<IrInstructionErrWrapPayload *>(instruction)->result_loc != nullptr;
|
|
case IrInstructionIdErrWrapCode:
|
|
return reinterpret_cast<IrInstructionErrWrapCode *>(instruction)->result_loc != nullptr;
|
|
case IrInstructionIdLoadPtrGen:
|
|
return reinterpret_cast<IrInstructionLoadPtrGen *>(instruction)->result_loc != nullptr;
|
|
case IrInstructionIdRefGen:
|
|
return reinterpret_cast<IrInstructionRefGen *>(instruction)->result_loc != nullptr;
|
|
}
|
|
zig_unreachable();
|
|
}
|