Previously, global assembly was parsed expecting it to have the template syntax. However global assembly has no inputs, outputs, or clobbers, and thus does not have template syntax. This is now fixed. This commit also adds a compile error for using volatile on global assembly, since it is meaningless. closes #1515
23190 lines
1.0 MiB
23190 lines
1.0 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;
|
|
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 *async_allocator_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);
|
|
static IrInstruction *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction);
|
|
static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, ZigType *expected_type);
|
|
static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *ptr);
|
|
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);
|
|
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);
|
|
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 void ir_add_alloca(IrAnalyze *ira, IrInstruction *instruction, ZigType *type_entry);
|
|
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 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 (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);
|
|
}
|
|
|
|
// 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 ZigTypeIdPointer:
|
|
case ZigTypeIdUndefined:
|
|
case ZigTypeIdNull:
|
|
case ZigTypeIdBoundFn:
|
|
case ZigTypeIdErrorSet:
|
|
case ZigTypeIdOpaque:
|
|
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 ZigTypeIdArray:
|
|
case ZigTypeIdStruct:
|
|
case ZigTypeIdOptional:
|
|
case ZigTypeIdErrorUnion:
|
|
case ZigTypeIdEnum:
|
|
case ZigTypeIdUnion:
|
|
case ZigTypeIdFn:
|
|
case ZigTypeIdArgTuple:
|
|
case ZigTypeIdPromise:
|
|
case ZigTypeIdVector:
|
|
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;
|
|
}
|
|
|
|
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);
|
|
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(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(IrInstructionCall *) {
|
|
return IrInstructionIdCall;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionConst *) {
|
|
return IrInstructionIdConst;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionReturn *) {
|
|
return IrInstructionIdReturn;
|
|
}
|
|
|
|
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(IrInstructionToPtrType *) {
|
|
return IrInstructionIdToPtrType;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrTypeChild *) {
|
|
return IrInstructionIdPtrTypeChild;
|
|
}
|
|
|
|
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(IrInstructionPromiseType *) {
|
|
return IrInstructionIdPromiseType;
|
|
}
|
|
|
|
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(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(IrInstructionStructInit *) {
|
|
return IrInstructionIdStructInit;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionUnionInit *) {
|
|
return IrInstructionIdUnionInit;
|
|
}
|
|
|
|
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(IrInstructionSlice *) {
|
|
return IrInstructionIdSlice;
|
|
}
|
|
|
|
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(IrInstructionHandle *) {
|
|
return IrInstructionIdHandle;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionAlignOf *) {
|
|
return IrInstructionIdAlignOf;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionOverflowOp *) {
|
|
return IrInstructionIdOverflowOp;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionTestErr *) {
|
|
return IrInstructionIdTestErr;
|
|
}
|
|
|
|
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(IrInstructionBitCast *) {
|
|
return IrInstructionIdBitCast;
|
|
}
|
|
|
|
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(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(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(IrInstructionGetImplicitAllocator *) {
|
|
return IrInstructionIdGetImplicitAllocator;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroId *) {
|
|
return IrInstructionIdCoroId;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroAlloc *) {
|
|
return IrInstructionIdCoroAlloc;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroSize *) {
|
|
return IrInstructionIdCoroSize;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroBegin *) {
|
|
return IrInstructionIdCoroBegin;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroAllocFail *) {
|
|
return IrInstructionIdCoroAllocFail;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroSuspend *) {
|
|
return IrInstructionIdCoroSuspend;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroEnd *) {
|
|
return IrInstructionIdCoroEnd;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroFree *) {
|
|
return IrInstructionIdCoroFree;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroResume *) {
|
|
return IrInstructionIdCoroResume;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroSave *) {
|
|
return IrInstructionIdCoroSave;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroPromise *) {
|
|
return IrInstructionIdCoroPromise;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionCoroAllocHelper *) {
|
|
return IrInstructionIdCoroAllocHelper;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicRmw *) {
|
|
return IrInstructionIdAtomicRmw;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicLoad *) {
|
|
return IrInstructionIdAtomicLoad;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionPromiseResultType *) {
|
|
return IrInstructionIdPromiseResultType;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionAwaitBookkeeping *) {
|
|
return IrInstructionIdAwaitBookkeeping;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionSaveErrRetAddr *) {
|
|
return IrInstructionIdSaveErrRetAddr;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionAddImplicitReturnType *) {
|
|
return IrInstructionIdAddImplicitReturnType;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionMergeErrRetTraces *) {
|
|
return IrInstructionIdMergeErrRetTraces;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionMarkErrRetTracePtr *) {
|
|
return IrInstructionIdMarkErrRetTracePtr;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionSqrt *) {
|
|
return IrInstructionIdSqrt;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionBswap *) {
|
|
return IrInstructionIdBswap;
|
|
}
|
|
|
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionBitReverse *) {
|
|
return IrInstructionIdBitReverse;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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 *return_value) {
|
|
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->value = return_value;
|
|
|
|
ir_ref_instruction(return_value, 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_build_const_u8(IrBuilder *irb, Scope *scope, AstNode *source_node, uint8_t value) {
|
|
IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
|
|
const_instruction->base.value.type = irb->codegen->builtin_types.entry_u8;
|
|
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_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_elem_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *array_ptr,
|
|
IrInstruction *elem_index, bool safety_check_on, PtrLen ptr_len)
|
|
{
|
|
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;
|
|
|
|
ir_ref_instruction(array_ptr, irb->current_basic_block);
|
|
ir_ref_instruction(elem_index, 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)
|
|
{
|
|
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;
|
|
|
|
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)
|
|
{
|
|
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;
|
|
|
|
ir_ref_instruction(container_ptr, 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)
|
|
{
|
|
IrInstructionUnionFieldPtr *instruction = ir_build_instruction<IrInstructionUnionFieldPtr>(irb, scope, source_node);
|
|
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(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 *async_allocator,
|
|
IrInstruction *new_stack)
|
|
{
|
|
IrInstructionCall *call_instruction = ir_build_instruction<IrInstructionCall>(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->async_allocator = async_allocator;
|
|
call_instruction->new_stack = new_stack;
|
|
|
|
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 (async_allocator != nullptr) ir_ref_instruction(async_allocator, irb->current_basic_block);
|
|
if (new_stack != nullptr) ir_ref_instruction(new_stack, irb->current_basic_block);
|
|
|
|
return &call_instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_phi(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
size_t incoming_count, IrBasicBlock **incoming_blocks, IrInstruction **incoming_values)
|
|
{
|
|
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;
|
|
|
|
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)
|
|
{
|
|
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;
|
|
|
|
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)
|
|
{
|
|
IrInstructionUnOp *instruction = ir_build_instruction<IrInstructionUnOp>(irb, scope, source_node);
|
|
instruction->op_id = op_id;
|
|
instruction->value = value;
|
|
instruction->lval = lval;
|
|
|
|
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);
|
|
}
|
|
|
|
static IrInstruction *ir_build_container_init_list(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *container_type, size_t item_count, IrInstruction **items)
|
|
{
|
|
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->items = items;
|
|
|
|
if (container_type != nullptr) ir_ref_instruction(container_type, irb->current_basic_block);
|
|
for (size_t i = 0; i < item_count; i += 1) {
|
|
ir_ref_instruction(items[i], 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)
|
|
{
|
|
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;
|
|
|
|
ir_ref_instruction(container_type, irb->current_basic_block);
|
|
for (size_t i = 0; i < field_count; i += 1) {
|
|
ir_ref_instruction(fields[i].value, irb->current_basic_block);
|
|
}
|
|
|
|
return &container_init_fields_instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_struct_init(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
ZigType *struct_type, size_t field_count, IrInstructionStructInitField *fields)
|
|
{
|
|
IrInstructionStructInit *struct_init_instruction = ir_build_instruction<IrInstructionStructInit>(irb, scope, source_node);
|
|
struct_init_instruction->struct_type = struct_type;
|
|
struct_init_instruction->field_count = field_count;
|
|
struct_init_instruction->fields = fields;
|
|
|
|
for (size_t i = 0; i < field_count; i += 1)
|
|
ir_ref_instruction(fields[i].value, irb->current_basic_block);
|
|
|
|
return &struct_init_instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_union_init(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
ZigType *union_type, TypeUnionField *field, IrInstruction *init_value)
|
|
{
|
|
IrInstructionUnionInit *union_init_instruction = ir_build_instruction<IrInstructionUnionInit>(irb, scope, source_node);
|
|
union_init_instruction->union_type = union_type;
|
|
union_init_instruction->field = field;
|
|
union_init_instruction->init_value = init_value;
|
|
|
|
ir_ref_instruction(init_value, irb->current_basic_block);
|
|
|
|
return &union_init_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 IrInstruction *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->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_var_decl_src(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
ZigVar *var, IrInstruction *var_type, IrInstruction *align_value, IrInstruction *init_value)
|
|
{
|
|
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->var_type = var_type;
|
|
decl_var_instruction->align_value = align_value;
|
|
decl_var_instruction->init_value = init_value;
|
|
|
|
if (var_type != nullptr) ir_ref_instruction(var_type, irb->current_basic_block);
|
|
if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block);
|
|
ir_ref_instruction(init_value, irb->current_basic_block);
|
|
|
|
return &decl_var_instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_var_decl_gen(IrAnalyze *ira, IrInstruction *source_instruction,
|
|
ZigVar *var, IrInstruction *init_value)
|
|
{
|
|
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->init_value = init_value;
|
|
|
|
ir_ref_instruction(init_value, 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)
|
|
{
|
|
IrInstructionResizeSlice *instruction = ir_build_instruction<IrInstructionResizeSlice>(&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_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_to_ptr_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
|
|
IrInstructionToPtrType *instruction = ir_build_instruction<IrInstructionToPtrType>(irb, scope, source_node);
|
|
instruction->value = value;
|
|
|
|
ir_ref_instruction(value, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_ptr_type_child(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *value)
|
|
{
|
|
IrInstructionPtrTypeChild *instruction = ir_build_instruction<IrInstructionPtrTypeChild>(
|
|
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_promise_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *payload_type)
|
|
{
|
|
IrInstructionPromiseType *instruction = ir_build_instruction<IrInstructionPromiseType>(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)
|
|
{
|
|
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;
|
|
|
|
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)
|
|
{
|
|
IrInstructionOptionalUnwrapPtr *instruction = ir_build_instruction<IrInstructionOptionalUnwrapPtr>(irb, scope, source_node);
|
|
instruction->base_ptr = base_ptr;
|
|
instruction->safety_check_on = safety_check_on;
|
|
|
|
ir_ref_instruction(base_ptr, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_maybe_wrap(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
|
|
IrInstructionOptionalWrap *instruction = ir_build_instruction<IrInstructionOptionalWrap>(irb, scope, source_node);
|
|
instruction->value = value;
|
|
|
|
ir_ref_instruction(value, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_err_wrap_payload(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
|
|
IrInstructionErrWrapPayload *instruction = ir_build_instruction<IrInstructionErrWrapPayload>(irb, scope, source_node);
|
|
instruction->value = value;
|
|
|
|
ir_ref_instruction(value, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_err_wrap_code(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
|
|
IrInstructionErrWrapCode *instruction = ir_build_instruction<IrInstructionErrWrapCode>(irb, scope, source_node);
|
|
instruction->value = value;
|
|
|
|
ir_ref_instruction(value, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_clz(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
|
|
IrInstructionClz *instruction = ir_build_instruction<IrInstructionClz>(irb, scope, source_node);
|
|
instruction->value = value;
|
|
|
|
ir_ref_instruction(value, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_ctz(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
|
|
IrInstructionCtz *instruction = ir_build_instruction<IrInstructionCtz>(irb, scope, source_node);
|
|
instruction->value = value;
|
|
|
|
ir_ref_instruction(value, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_pop_count(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
|
|
IrInstructionPopCount *instruction = ir_build_instruction<IrInstructionPopCount>(irb, scope, source_node);
|
|
instruction->value = value;
|
|
|
|
ir_ref_instruction(value, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *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->base;
|
|
}
|
|
|
|
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 *prong_value)
|
|
{
|
|
IrInstructionSwitchVar *instruction = ir_build_instruction<IrInstructionSwitchVar>(irb, scope, source_node);
|
|
instruction->target_value_ptr = target_value_ptr;
|
|
instruction->prong_value = prong_value;
|
|
|
|
ir_ref_instruction(target_value_ptr, irb->current_basic_block);
|
|
ir_ref_instruction(prong_value, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
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_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)
|
|
{
|
|
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;
|
|
|
|
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,
|
|
IrInstruction *ptr, IrInstruction *cmp_value, IrInstruction *new_value,
|
|
AtomicOrder success_order, AtomicOrder failure_order, bool is_weak)
|
|
{
|
|
IrInstructionCmpxchgGen *instruction = ir_build_instruction<IrInstructionCmpxchgGen>(&ira->new_irb,
|
|
source_instruction->scope, source_instruction->source_node);
|
|
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;
|
|
|
|
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);
|
|
|
|
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) {
|
|
IrInstructionToBytes *instruction = ir_build_instruction<IrInstructionToBytes>(irb, scope, source_node);
|
|
instruction->target = target;
|
|
|
|
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) {
|
|
IrInstructionFromBytes *instruction = ir_build_instruction<IrInstructionFromBytes>(irb, scope, source_node);
|
|
instruction->dest_child_type = dest_child_type;
|
|
instruction->target = target;
|
|
|
|
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(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *ptr, IrInstruction *start, IrInstruction *end, bool safety_check_on)
|
|
{
|
|
IrInstructionSlice *instruction = ir_build_instruction<IrInstructionSlice>(irb, scope, source_node);
|
|
instruction->ptr = ptr;
|
|
instruction->start = start;
|
|
instruction->end = end;
|
|
instruction->safety_check_on = safety_check_on;
|
|
|
|
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_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) {
|
|
IrInstructionHandle *instruction = ir_build_instruction<IrInstructionHandle>(irb, scope, source_node);
|
|
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;
|
|
}
|
|
|
|
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(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *value)
|
|
{
|
|
IrInstructionTestErr *instruction = ir_build_instruction<IrInstructionTestErr>(irb, scope, source_node);
|
|
instruction->value = value;
|
|
|
|
ir_ref_instruction(value, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_unwrap_err_code(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *err_union)
|
|
{
|
|
IrInstructionUnwrapErrCode *instruction = ir_build_instruction<IrInstructionUnwrapErrCode>(irb, scope, source_node);
|
|
instruction->err_union = err_union;
|
|
|
|
ir_ref_instruction(err_union, 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)
|
|
{
|
|
IrInstructionUnwrapErrPayload *instruction = ir_build_instruction<IrInstructionUnwrapErrPayload>(irb, scope, source_node);
|
|
instruction->value = value;
|
|
instruction->safety_check_on = safety_check_on;
|
|
|
|
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,
|
|
IrInstruction *async_allocator_type_value, 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->async_allocator_type_value = async_allocator_type_value;
|
|
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);
|
|
if (async_allocator_type_value != nullptr) ir_ref_instruction(async_allocator_type_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)
|
|
{
|
|
IrInstructionLoadPtrGen *instruction = ir_build_instruction<IrInstructionLoadPtrGen>(
|
|
&ira->new_irb, source_instruction->scope, source_instruction->source_node);
|
|
instruction->base.value.type = ty;
|
|
instruction->ptr = ptr;
|
|
|
|
ir_ref_instruction(ptr, ira->new_irb.current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_bit_cast(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *dest_type, IrInstruction *value)
|
|
{
|
|
IrInstructionBitCast *instruction = ir_build_instruction<IrInstructionBitCast>(
|
|
irb, scope, source_node);
|
|
instruction->dest_type = dest_type;
|
|
instruction->value = value;
|
|
|
|
ir_ref_instruction(dest_type, irb->current_basic_block);
|
|
ir_ref_instruction(value, 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_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_cancel(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *target)
|
|
{
|
|
IrInstructionCancel *instruction = ir_build_instruction<IrInstructionCancel>(irb, scope, source_node);
|
|
instruction->target = target;
|
|
|
|
ir_ref_instruction(target, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_get_implicit_allocator(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
ImplicitAllocatorId id)
|
|
{
|
|
IrInstructionGetImplicitAllocator *instruction = ir_build_instruction<IrInstructionGetImplicitAllocator>(irb, scope, source_node);
|
|
instruction->id = id;
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_coro_id(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *promise_ptr) {
|
|
IrInstructionCoroId *instruction = ir_build_instruction<IrInstructionCoroId>(irb, scope, source_node);
|
|
instruction->promise_ptr = promise_ptr;
|
|
|
|
ir_ref_instruction(promise_ptr, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_coro_alloc(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *coro_id) {
|
|
IrInstructionCoroAlloc *instruction = ir_build_instruction<IrInstructionCoroAlloc>(irb, scope, source_node);
|
|
instruction->coro_id = coro_id;
|
|
|
|
ir_ref_instruction(coro_id, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_coro_size(IrBuilder *irb, Scope *scope, AstNode *source_node) {
|
|
IrInstructionCoroSize *instruction = ir_build_instruction<IrInstructionCoroSize>(irb, scope, source_node);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_coro_begin(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *coro_id, IrInstruction *coro_mem_ptr) {
|
|
IrInstructionCoroBegin *instruction = ir_build_instruction<IrInstructionCoroBegin>(irb, scope, source_node);
|
|
instruction->coro_id = coro_id;
|
|
instruction->coro_mem_ptr = coro_mem_ptr;
|
|
|
|
ir_ref_instruction(coro_id, irb->current_basic_block);
|
|
ir_ref_instruction(coro_mem_ptr, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_coro_alloc_fail(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *err_val) {
|
|
IrInstructionCoroAllocFail *instruction = ir_build_instruction<IrInstructionCoroAllocFail>(irb, scope, source_node);
|
|
instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable;
|
|
instruction->base.value.special = ConstValSpecialStatic;
|
|
instruction->err_val = err_val;
|
|
|
|
ir_ref_instruction(err_val, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_coro_suspend(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *save_point, IrInstruction *is_final)
|
|
{
|
|
IrInstructionCoroSuspend *instruction = ir_build_instruction<IrInstructionCoroSuspend>(irb, scope, source_node);
|
|
instruction->save_point = save_point;
|
|
instruction->is_final = is_final;
|
|
|
|
if (save_point != nullptr) ir_ref_instruction(save_point, irb->current_basic_block);
|
|
ir_ref_instruction(is_final, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_coro_end(IrBuilder *irb, Scope *scope, AstNode *source_node) {
|
|
IrInstructionCoroEnd *instruction = ir_build_instruction<IrInstructionCoroEnd>(irb, scope, source_node);
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_coro_free(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *coro_id, IrInstruction *coro_handle)
|
|
{
|
|
IrInstructionCoroFree *instruction = ir_build_instruction<IrInstructionCoroFree>(irb, scope, source_node);
|
|
instruction->coro_id = coro_id;
|
|
instruction->coro_handle = coro_handle;
|
|
|
|
ir_ref_instruction(coro_id, irb->current_basic_block);
|
|
ir_ref_instruction(coro_handle, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_coro_resume(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *awaiter_handle)
|
|
{
|
|
IrInstructionCoroResume *instruction = ir_build_instruction<IrInstructionCoroResume>(irb, scope, source_node);
|
|
instruction->awaiter_handle = awaiter_handle;
|
|
|
|
ir_ref_instruction(awaiter_handle, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_coro_save(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *coro_handle)
|
|
{
|
|
IrInstructionCoroSave *instruction = ir_build_instruction<IrInstructionCoroSave>(irb, scope, source_node);
|
|
instruction->coro_handle = coro_handle;
|
|
|
|
ir_ref_instruction(coro_handle, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_coro_promise(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *coro_handle)
|
|
{
|
|
IrInstructionCoroPromise *instruction = ir_build_instruction<IrInstructionCoroPromise>(irb, scope, source_node);
|
|
instruction->coro_handle = coro_handle;
|
|
|
|
ir_ref_instruction(coro_handle, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_coro_alloc_helper(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *realloc_fn, IrInstruction *coro_size)
|
|
{
|
|
IrInstructionCoroAllocHelper *instruction = ir_build_instruction<IrInstructionCoroAllocHelper>(irb, scope, source_node);
|
|
instruction->realloc_fn = realloc_fn;
|
|
instruction->coro_size = coro_size;
|
|
|
|
ir_ref_instruction(realloc_fn, irb->current_basic_block);
|
|
ir_ref_instruction(coro_size, 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_promise_result_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *promise_type)
|
|
{
|
|
IrInstructionPromiseResultType *instruction = ir_build_instruction<IrInstructionPromiseResultType>(irb, scope, source_node);
|
|
instruction->promise_type = promise_type;
|
|
|
|
ir_ref_instruction(promise_type, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_await_bookkeeping(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *promise_result_type)
|
|
{
|
|
IrInstructionAwaitBookkeeping *instruction = ir_build_instruction<IrInstructionAwaitBookkeeping>(irb, scope, source_node);
|
|
instruction->promise_result_type = promise_result_type;
|
|
|
|
ir_ref_instruction(promise_result_type, 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_merge_err_ret_traces(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
|
IrInstruction *coro_promise_ptr, IrInstruction *src_err_ret_trace_ptr, IrInstruction *dest_err_ret_trace_ptr)
|
|
{
|
|
IrInstructionMergeErrRetTraces *instruction = ir_build_instruction<IrInstructionMergeErrRetTraces>(irb, scope, source_node);
|
|
instruction->coro_promise_ptr = coro_promise_ptr;
|
|
instruction->src_err_ret_trace_ptr = src_err_ret_trace_ptr;
|
|
instruction->dest_err_ret_trace_ptr = dest_err_ret_trace_ptr;
|
|
|
|
ir_ref_instruction(coro_promise_ptr, irb->current_basic_block);
|
|
ir_ref_instruction(src_err_ret_trace_ptr, irb->current_basic_block);
|
|
ir_ref_instruction(dest_err_ret_trace_ptr, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_mark_err_ret_trace_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *err_ret_trace_ptr) {
|
|
IrInstructionMarkErrRetTracePtr *instruction = ir_build_instruction<IrInstructionMarkErrRetTracePtr>(irb, scope, source_node);
|
|
instruction->err_ret_trace_ptr = err_ret_trace_ptr;
|
|
|
|
ir_ref_instruction(err_ret_trace_ptr, irb->current_basic_block);
|
|
|
|
return &instruction->base;
|
|
}
|
|
|
|
static IrInstruction *ir_build_sqrt(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type, IrInstruction *op) {
|
|
IrInstructionSqrt *instruction = ir_build_instruction<IrInstructionSqrt>(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 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_vector_to_array(IrAnalyze *ira, IrInstruction *source_instruction,
|
|
IrInstruction *vector, ZigType *result_type)
|
|
{
|
|
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;
|
|
|
|
ir_ref_instruction(vector, ira->new_irb.current_basic_block);
|
|
|
|
ir_add_alloca(ira, &instruction->base, result_type);
|
|
|
|
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 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:
|
|
case ScopeIdCoroPrelude:
|
|
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:
|
|
case ScopeIdCoroPrelude:
|
|
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) {
|
|
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 bool exec_is_async(IrExecutable *exec) {
|
|
ZigFn *fn_entry = exec_fn_entry(exec);
|
|
return fn_entry != nullptr && fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync;
|
|
}
|
|
|
|
static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *return_value,
|
|
bool is_generated_code)
|
|
{
|
|
ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, return_value));
|
|
|
|
bool is_async = exec_is_async(irb->exec);
|
|
if (!is_async) {
|
|
IrInstruction *return_inst = ir_build_return(irb, scope, node, return_value);
|
|
return_inst->is_gen = is_generated_code;
|
|
return return_inst;
|
|
}
|
|
|
|
IrBasicBlock *suspended_block = ir_create_basic_block(irb, scope, "Suspended");
|
|
IrBasicBlock *not_suspended_block = ir_create_basic_block(irb, scope, "NotSuspended");
|
|
IrBasicBlock *store_awaiter_block = ir_create_basic_block(irb, scope, "StoreAwaiter");
|
|
IrBasicBlock *check_canceled_block = ir_create_basic_block(irb, scope, "CheckCanceled");
|
|
|
|
IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111
|
|
IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000
|
|
IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001
|
|
IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010
|
|
IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_promise);
|
|
IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false);
|
|
IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
|
|
|
|
ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_field_ptr, return_value);
|
|
IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize);
|
|
IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
|
|
usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, ptr_mask, nullptr,
|
|
AtomicRmwOp_or, AtomicOrderSeqCst);
|
|
|
|
IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false);
|
|
IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false);
|
|
ir_build_cond_br(irb, scope, node, is_suspended_bool, suspended_block, not_suspended_block, is_comptime);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, suspended_block);
|
|
ir_build_unreachable(irb, scope, node);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, not_suspended_block);
|
|
IrInstruction *await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false);
|
|
// if we ever add null checking safety to the ptrtoint instruction, it needs to be disabled here
|
|
IrInstruction *have_await_handle = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false);
|
|
ir_build_cond_br(irb, scope, node, have_await_handle, store_awaiter_block, check_canceled_block, is_comptime);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, store_awaiter_block);
|
|
IrInstruction *await_handle = ir_build_int_to_ptr(irb, scope, node, promise_type_val, await_handle_addr);
|
|
ir_build_store_ptr(irb, scope, node, irb->exec->await_handle_var_ptr, await_handle);
|
|
ir_build_br(irb, scope, node, irb->exec->coro_normal_final, is_comptime);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, check_canceled_block);
|
|
IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false);
|
|
IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false);
|
|
return ir_build_cond_br(irb, scope, node, is_canceled_bool, irb->exec->coro_final_cleanup_block, irb->exec->coro_early_final, is_comptime);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) {
|
|
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:
|
|
{
|
|
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(irb, expr_node, scope);
|
|
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);
|
|
}
|
|
|
|
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");
|
|
if (!have_err_defers) {
|
|
ir_gen_defers_for_block(irb, scope, outer_scope, false);
|
|
}
|
|
|
|
IrInstruction *is_err = ir_build_test_err(irb, scope, node, return_value);
|
|
|
|
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 (have_err_defers) {
|
|
ir_gen_defers_for_block(irb, scope, outer_scope, true);
|
|
}
|
|
if (irb->codegen->have_err_ret_tracing && !should_inline) {
|
|
ir_build_save_err_ret_addr(irb, scope, node);
|
|
}
|
|
ir_build_br(irb, scope, node, ret_stmt_block, is_comptime);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, ok_block);
|
|
if (have_err_defers) {
|
|
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);
|
|
return ir_gen_async_return(irb, scope, node, return_value, false);
|
|
} else {
|
|
// generate unconditional defers
|
|
ir_gen_defers_for_block(irb, scope, outer_scope, false);
|
|
return ir_gen_async_return(irb, scope, node, return_value, false);
|
|
}
|
|
}
|
|
case ReturnKindError:
|
|
{
|
|
assert(expr_node);
|
|
IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr);
|
|
if (err_union_ptr == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
IrInstruction *err_union_val = ir_build_load_ptr(irb, scope, node, err_union_ptr);
|
|
IrInstruction *is_err_val = ir_build_test_err(irb, scope, node, err_union_val);
|
|
|
|
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);
|
|
if (!ir_gen_defers_for_block(irb, scope, outer_scope, true)) {
|
|
IrInstruction *err_val = ir_build_unwrap_err_code(irb, scope, node, err_union_ptr);
|
|
if (irb->codegen->have_err_ret_tracing && !should_inline) {
|
|
ir_build_save_err_ret_addr(irb, scope, node);
|
|
}
|
|
ir_gen_async_return(irb, scope, node, err_val, false);
|
|
}
|
|
|
|
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);
|
|
if (lval == LValPtr)
|
|
return unwrapped_ptr;
|
|
else
|
|
return ir_build_load_ptr(irb, scope, node, unwrapped_ptr);
|
|
}
|
|
}
|
|
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 IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode *block_node) {
|
|
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_build_const_void(irb, child_scope, block_node);
|
|
}
|
|
|
|
if (block_node->data.block.name != nullptr) {
|
|
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));
|
|
}
|
|
|
|
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;
|
|
}
|
|
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;
|
|
}
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, scope_block->end_block);
|
|
return ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
|
|
} else {
|
|
incoming_blocks.append(irb->current_basic_block);
|
|
incoming_values.append(ir_mark_gen(ir_build_const_void(irb, parent_scope, block_node)));
|
|
}
|
|
|
|
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);
|
|
return ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
|
|
} else {
|
|
ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false);
|
|
return ir_mark_gen(ir_mark_gen(ir_build_const_void(irb, child_scope, block_node)));
|
|
}
|
|
}
|
|
|
|
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);
|
|
IrInstruction *rvalue = ir_gen_node(irb, node->data.bin_op_expr.op2, scope);
|
|
|
|
if (lvalue == irb->codegen->invalid_instruction || rvalue == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
ir_build_store_ptr(irb, scope, node, lvalue, rvalue);
|
|
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);
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_orelse(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 *maybe_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LValPtr);
|
|
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");
|
|
ir_build_cond_br(irb, parent_scope, node, is_non_null, ok_block, null_block, is_comptime);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, null_block);
|
|
IrInstruction *null_result = ir_gen_node(irb, op2_node, parent_scope);
|
|
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);
|
|
IrInstruction *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr);
|
|
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;
|
|
return ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values);
|
|
}
|
|
|
|
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) {
|
|
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_gen_assign(irb, scope, node);
|
|
case BinOpTypeAssignTimes:
|
|
return ir_gen_assign_op(irb, scope, node, IrBinOpMult);
|
|
case BinOpTypeAssignTimesWrap:
|
|
return ir_gen_assign_op(irb, scope, node, IrBinOpMultWrap);
|
|
case BinOpTypeAssignDiv:
|
|
return ir_gen_assign_op(irb, scope, node, IrBinOpDivUnspecified);
|
|
case BinOpTypeAssignMod:
|
|
return ir_gen_assign_op(irb, scope, node, IrBinOpRemUnspecified);
|
|
case BinOpTypeAssignPlus:
|
|
return ir_gen_assign_op(irb, scope, node, IrBinOpAdd);
|
|
case BinOpTypeAssignPlusWrap:
|
|
return ir_gen_assign_op(irb, scope, node, IrBinOpAddWrap);
|
|
case BinOpTypeAssignMinus:
|
|
return ir_gen_assign_op(irb, scope, node, IrBinOpSub);
|
|
case BinOpTypeAssignMinusWrap:
|
|
return ir_gen_assign_op(irb, scope, node, IrBinOpSubWrap);
|
|
case BinOpTypeAssignBitShiftLeft:
|
|
return ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftLeftLossy);
|
|
case BinOpTypeAssignBitShiftRight:
|
|
return ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftRightLossy);
|
|
case BinOpTypeAssignBitAnd:
|
|
return ir_gen_assign_op(irb, scope, node, IrBinOpBinAnd);
|
|
case BinOpTypeAssignBitXor:
|
|
return ir_gen_assign_op(irb, scope, node, IrBinOpBinXor);
|
|
case BinOpTypeAssignBitOr:
|
|
return ir_gen_assign_op(irb, scope, node, IrBinOpBinOr);
|
|
case BinOpTypeAssignMergeErrorSets:
|
|
return ir_gen_assign_op(irb, scope, node, IrBinOpMergeErrorSets);
|
|
case BinOpTypeBoolOr:
|
|
return ir_gen_bool_or(irb, scope, node);
|
|
case BinOpTypeBoolAnd:
|
|
return ir_gen_bool_and(irb, scope, node);
|
|
case BinOpTypeCmpEq:
|
|
return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpEq);
|
|
case BinOpTypeCmpNotEq:
|
|
return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpNotEq);
|
|
case BinOpTypeCmpLessThan:
|
|
return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessThan);
|
|
case BinOpTypeCmpGreaterThan:
|
|
return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterThan);
|
|
case BinOpTypeCmpLessOrEq:
|
|
return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessOrEq);
|
|
case BinOpTypeCmpGreaterOrEq:
|
|
return ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterOrEq);
|
|
case BinOpTypeBinOr:
|
|
return ir_gen_bin_op_id(irb, scope, node, IrBinOpBinOr);
|
|
case BinOpTypeBinXor:
|
|
return ir_gen_bin_op_id(irb, scope, node, IrBinOpBinXor);
|
|
case BinOpTypeBinAnd:
|
|
return ir_gen_bin_op_id(irb, scope, node, IrBinOpBinAnd);
|
|
case BinOpTypeBitShiftLeft:
|
|
return ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftLeftLossy);
|
|
case BinOpTypeBitShiftRight:
|
|
return ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftRightLossy);
|
|
case BinOpTypeAdd:
|
|
return ir_gen_bin_op_id(irb, scope, node, IrBinOpAdd);
|
|
case BinOpTypeAddWrap:
|
|
return ir_gen_bin_op_id(irb, scope, node, IrBinOpAddWrap);
|
|
case BinOpTypeSub:
|
|
return ir_gen_bin_op_id(irb, scope, node, IrBinOpSub);
|
|
case BinOpTypeSubWrap:
|
|
return ir_gen_bin_op_id(irb, scope, node, IrBinOpSubWrap);
|
|
case BinOpTypeMult:
|
|
return ir_gen_bin_op_id(irb, scope, node, IrBinOpMult);
|
|
case BinOpTypeMultWrap:
|
|
return ir_gen_bin_op_id(irb, scope, node, IrBinOpMultWrap);
|
|
case BinOpTypeDiv:
|
|
return ir_gen_bin_op_id(irb, scope, node, IrBinOpDivUnspecified);
|
|
case BinOpTypeMod:
|
|
return ir_gen_bin_op_id(irb, scope, node, IrBinOpRemUnspecified);
|
|
case BinOpTypeArrayCat:
|
|
return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayCat);
|
|
case BinOpTypeArrayMult:
|
|
return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayMult);
|
|
case BinOpTypeMergeErrorSets:
|
|
return ir_gen_bin_op_id(irb, scope, node, IrBinOpMergeErrorSets);
|
|
case BinOpTypeUnwrapOptional:
|
|
return ir_gen_orelse(irb, scope, node);
|
|
case BinOpTypeErrorUnion:
|
|
return ir_gen_error_union(irb, scope, node);
|
|
}
|
|
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) {
|
|
Error err;
|
|
assert(node->type == NodeTypeSymbol);
|
|
|
|
Buf *variable_name = node->data.symbol_expr.symbol;
|
|
|
|
if (buf_eql_str(variable_name, "_") && 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;
|
|
}
|
|
|
|
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 value;
|
|
}
|
|
}
|
|
|
|
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_build_load_ptr(irb, scope, node, var_ptr);
|
|
}
|
|
|
|
Tld *tld = find_decl(irb->codegen, scope, variable_name);
|
|
if (tld)
|
|
return ir_build_decl_ref(irb, scope, node, tld, lval);
|
|
|
|
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;
|
|
}
|
|
|
|
// 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(irb->codegen, scope, node, variable_name);
|
|
add_node_error(irb->codegen, node, buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name)));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
|
|
static IrInstruction *ir_gen_array_access(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) {
|
|
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);
|
|
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);
|
|
if (lval == LValPtr)
|
|
return ptr_instruction;
|
|
|
|
return ir_build_load_ptr(irb, scope, node, ptr_instruction);
|
|
}
|
|
|
|
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);
|
|
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);
|
|
}
|
|
|
|
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_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) {
|
|
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;
|
|
}
|
|
|
|
bool is_async = exec_is_async(irb->exec);
|
|
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
case BuiltinFnIdCtz:
|
|
{
|
|
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 *ctz = ir_build_ctz(irb, scope, node, arg0_value);
|
|
return ir_lval_wrap(irb, scope, ctz, lval);
|
|
}
|
|
case BuiltinFnIdPopCount:
|
|
{
|
|
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 *instr = ir_build_pop_count(irb, scope, node, arg0_value);
|
|
return ir_lval_wrap(irb, scope, instr, lval);
|
|
}
|
|
case BuiltinFnIdClz:
|
|
{
|
|
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 *clz = ir_build_clz(irb, scope, node, arg0_value);
|
|
return ir_lval_wrap(irb, scope, clz, lval);
|
|
}
|
|
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);
|
|
}
|
|
case BuiltinFnIdCImport:
|
|
{
|
|
IrInstruction *c_import = ir_build_c_import(irb, scope, node);
|
|
return ir_lval_wrap(irb, scope, c_import, lval);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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));
|
|
return ir_lval_wrap(irb, scope, cmpxchg, lval);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
case BuiltinFnIdSqrt:
|
|
{
|
|
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_sqrt(irb, scope, node, arg0_value, arg1_value);
|
|
return ir_lval_wrap(irb, scope, ir_sqrt, lval);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
return ir_lval_wrap(irb, scope, result, lval);
|
|
}
|
|
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);
|
|
return ir_lval_wrap(irb, scope, result, lval);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
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);
|
|
|
|
if (lval == LValPtr)
|
|
return ptr_instruction;
|
|
|
|
return ir_build_load_ptr(irb, scope, node, ptr_instruction);
|
|
}
|
|
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);
|
|
}
|
|
case BuiltinFnIdBreakpoint:
|
|
return ir_lval_wrap(irb, scope, ir_build_breakpoint(irb, scope, node), lval);
|
|
case BuiltinFnIdReturnAddress:
|
|
return ir_lval_wrap(irb, scope, ir_build_return_address(irb, scope, node), lval);
|
|
case BuiltinFnIdFrameAddress:
|
|
return ir_lval_wrap(irb, scope, ir_build_frame_address(irb, scope, node), lval);
|
|
case BuiltinFnIdHandle:
|
|
if (!irb->exec->fn_entry) {
|
|
add_node_error(irb->codegen, node, buf_sprintf("@handle() called outside of function definition"));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
if (!is_async) {
|
|
add_node_error(irb->codegen, node, buf_sprintf("@handle() in non-async function"));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
return ir_lval_wrap(irb, scope, ir_build_handle(irb, scope, node), lval);
|
|
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);
|
|
}
|
|
case BuiltinFnIdAddWithOverflow:
|
|
return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpAdd), lval);
|
|
case BuiltinFnIdSubWithOverflow:
|
|
return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpSub), lval);
|
|
case BuiltinFnIdMulWithOverflow:
|
|
return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul), lval);
|
|
case BuiltinFnIdShlWithOverflow:
|
|
return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl), lval);
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
case BuiltinFnIdBitCast:
|
|
{
|
|
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 *bit_cast = ir_build_bit_cast(irb, scope, node, arg0_value, arg1_value);
|
|
return ir_lval_wrap(irb, scope, bit_cast, lval);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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(irb, scope, node, nullptr, fn_ref, arg_count, args, false, fn_inline, false, nullptr, nullptr);
|
|
return ir_lval_wrap(irb, scope, call, lval);
|
|
}
|
|
case BuiltinFnIdNewStackCall:
|
|
{
|
|
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 *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(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, false, nullptr, new_stack);
|
|
return ir_lval_wrap(irb, scope, call, lval);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
case BuiltinFnIdOpaqueType:
|
|
{
|
|
IrInstruction *opaque_type = ir_build_opaque_type(irb, scope, node);
|
|
return ir_lval_wrap(irb, scope, opaque_type, lval);
|
|
}
|
|
case BuiltinFnIdThis:
|
|
{
|
|
IrInstruction *this_inst = ir_gen_this(irb, scope, node);
|
|
return ir_lval_wrap(irb, scope, this_inst, lval);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
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;
|
|
|
|
return 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);
|
|
}
|
|
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;
|
|
|
|
return 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);
|
|
}
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
case BuiltinFnIdBswap:
|
|
{
|
|
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_bswap(irb, scope, node, arg0_value, arg1_value);
|
|
return ir_lval_wrap(irb, scope, result, lval);
|
|
}
|
|
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 = ir_build_bit_reverse(irb, scope, node, arg0_value, arg1_value);
|
|
return ir_lval_wrap(irb, scope, result, lval);
|
|
}
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) {
|
|
assert(node->type == NodeTypeFnCallExpr);
|
|
|
|
if (node->data.fn_call_expr.is_builtin)
|
|
return ir_gen_builtin_fn_call(irb, scope, node, lval);
|
|
|
|
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 *async_allocator = nullptr;
|
|
if (is_async) {
|
|
if (node->data.fn_call_expr.async_allocator) {
|
|
async_allocator = ir_gen_node(irb, node->data.fn_call_expr.async_allocator, scope);
|
|
if (async_allocator == irb->codegen->invalid_instruction)
|
|
return async_allocator;
|
|
}
|
|
}
|
|
|
|
IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto,
|
|
is_async, async_allocator, nullptr);
|
|
return ir_lval_wrap(irb, scope, fn_call, lval);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
|
|
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");
|
|
|
|
ir_build_cond_br(irb, scope, condition->source_node, condition, then_block, else_block, 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(irb, then_node, subexpr_scope);
|
|
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(irb, else_node, subexpr_scope);
|
|
if (else_expr_result == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
} else {
|
|
else_expr_result = ir_build_const_void(irb, scope, node);
|
|
}
|
|
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;
|
|
|
|
return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values);
|
|
}
|
|
|
|
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);
|
|
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_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval) {
|
|
if (lval != LValPtr)
|
|
return value;
|
|
if (value == irb->codegen->invalid_instruction)
|
|
return value;
|
|
|
|
// We needed a pointer to a value, but we got a value. So we create
|
|
// an instruction which just makes a const pointer of it.
|
|
return ir_build_ref(irb, scope, value->source_node, value, false, false);
|
|
}
|
|
|
|
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;
|
|
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);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_catch_unreachable(IrBuilder *irb, Scope *scope, AstNode *source_node, AstNode *expr_node,
|
|
LVal lval)
|
|
{
|
|
IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr);
|
|
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);
|
|
if (payload_ptr == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
if (lval == LValPtr)
|
|
return payload_ptr;
|
|
|
|
return ir_build_load_ptr(irb, scope, source_node, payload_ptr);
|
|
}
|
|
|
|
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) {
|
|
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);
|
|
case PrefixOpBinNot:
|
|
return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpBinNot), lval);
|
|
case PrefixOpNegation:
|
|
return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegation), lval);
|
|
case PrefixOpNegationWrap:
|
|
return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegationWrap), lval);
|
|
case PrefixOpOptional:
|
|
return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpOptional), lval);
|
|
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), lval);
|
|
}
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
|
|
assert(node->type == NodeTypeContainerInitExpr);
|
|
|
|
AstNodeContainerInitExpr *container_init_expr = &node->data.container_init_expr;
|
|
ContainerInitKind kind = container_init_expr->kind;
|
|
|
|
IrInstruction *container_type = ir_gen_node(irb, container_init_expr->type, scope);
|
|
if (container_type == irb->codegen->invalid_instruction)
|
|
return container_type;
|
|
|
|
if (kind == ContainerInitKindStruct) {
|
|
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 *expr_value = ir_gen_node(irb, expr_node, scope);
|
|
if (expr_value == irb->codegen->invalid_instruction)
|
|
return expr_value;
|
|
|
|
fields[i].name = name;
|
|
fields[i].value = expr_value;
|
|
fields[i].source_node = entry_node;
|
|
}
|
|
return ir_build_container_init_fields(irb, scope, node, container_type, field_count, fields);
|
|
} else if (kind == ContainerInitKindArray) {
|
|
size_t item_count = container_init_expr->entries.length;
|
|
IrInstruction **values = allocate<IrInstruction *>(item_count);
|
|
for (size_t i = 0; i < item_count; i += 1) {
|
|
AstNode *expr_node = container_init_expr->entries.at(i);
|
|
IrInstruction *expr_value = ir_gen_node(irb, expr_node, scope);
|
|
if (expr_value == irb->codegen->invalid_instruction)
|
|
return expr_value;
|
|
|
|
values[i] = expr_value;
|
|
}
|
|
return ir_build_container_init_list(irb, scope, node, container_type, item_count, values);
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
IrInstruction *type_instruction;
|
|
if (variable_declaration->type != nullptr) {
|
|
type_instruction = ir_gen_node(irb, variable_declaration->type, 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;
|
|
|
|
IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node,
|
|
ir_should_inline(irb->exec, scope) || variable_declaration->is_comptime);
|
|
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, 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)));
|
|
}
|
|
if (variable_declaration->threadlocal_tok != nullptr) {
|
|
add_token_error(irb->codegen, node->owner, variable_declaration->threadlocal_tok,
|
|
buf_sprintf("function-local variable '%s' cannot be threadlocal", buf_ptr(variable_declaration->symbol)));
|
|
}
|
|
|
|
// 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(irb, variable_declaration->expr, scope);
|
|
irb->exec->name = old_exec_name;
|
|
|
|
if (init_value == irb->codegen->invalid_instruction)
|
|
return init_value;
|
|
|
|
return ir_build_var_decl_src(irb, scope, node, var, type_instruction, align_value, init_value);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
|
|
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);
|
|
if (err_val_ptr == irb->codegen->invalid_instruction)
|
|
return err_val_ptr;
|
|
IrInstruction *err_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, err_val_ptr);
|
|
IrInstruction *is_err = ir_build_test_err(irb, scope, node->data.while_expr.condition, err_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));
|
|
if (!instr_is_unreachable(is_err)) {
|
|
ir_mark_gen(ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_err,
|
|
else_block, body_block, is_comptime));
|
|
}
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, body_block);
|
|
if (var_symbol) {
|
|
IrInstruction *var_ptr_value = ir_build_unwrap_err_payload(irb, payload_scope, symbol_node,
|
|
err_val_ptr, false);
|
|
IrInstruction *var_value = node->data.while_expr.var_is_ptr ?
|
|
var_ptr_value : ir_build_load_ptr(irb, payload_scope, symbol_node, var_ptr_value);
|
|
ir_build_var_decl_src(irb, payload_scope, symbol_node, payload_var, nullptr, nullptr, var_value);
|
|
}
|
|
|
|
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;
|
|
|
|
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_var_value = 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, nullptr, err_var_value);
|
|
|
|
IrInstruction *else_result = ir_gen_node(irb, else_node, err_scope);
|
|
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);
|
|
}
|
|
|
|
return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
|
|
} 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);
|
|
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));
|
|
if (!instr_is_unreachable(is_non_null)) {
|
|
ir_mark_gen(ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_non_null,
|
|
body_block, else_block, is_comptime));
|
|
}
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, body_block);
|
|
IrInstruction *var_ptr_value = ir_build_optional_unwrap_ptr(irb, child_scope, symbol_node, maybe_val_ptr, false);
|
|
IrInstruction *var_value = node->data.while_expr.var_is_ptr ?
|
|
var_ptr_value : ir_build_load_ptr(irb, child_scope, symbol_node, var_ptr_value);
|
|
ir_build_var_decl_src(irb, child_scope, symbol_node, payload_var, nullptr, nullptr, var_value);
|
|
|
|
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;
|
|
|
|
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);
|
|
|
|
else_result = ir_gen_node(irb, else_node, scope);
|
|
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);
|
|
}
|
|
|
|
return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
|
|
} 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));
|
|
if (!instr_is_unreachable(cond_val)) {
|
|
ir_mark_gen(ir_build_cond_br(irb, scope, node->data.while_expr.condition, cond_val,
|
|
body_block, else_block, 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;
|
|
|
|
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);
|
|
|
|
else_result = ir_gen_node(irb, else_node, subexpr_scope);
|
|
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);
|
|
}
|
|
|
|
return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
|
|
}
|
|
}
|
|
|
|
static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
|
|
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);
|
|
if (array_val_ptr == irb->codegen->invalid_instruction)
|
|
return array_val_ptr;
|
|
|
|
IrInstruction *array_val = ir_build_load_ptr(irb, parent_scope, array_node, array_val_ptr);
|
|
|
|
IrInstruction *pointer_type = ir_build_to_ptr_type(irb, parent_scope, array_node, array_val);
|
|
IrInstruction *elem_var_type;
|
|
if (node->data.for_expr.elem_is_ptr) {
|
|
elem_var_type = pointer_type;
|
|
} else {
|
|
elem_var_type = ir_build_ptr_type_child(irb, parent_scope, elem_node, pointer_type);
|
|
}
|
|
|
|
IrInstruction *is_comptime = ir_build_const_bool(irb, parent_scope, node,
|
|
ir_should_inline(irb->exec, parent_scope) || node->data.for_expr.is_inline);
|
|
|
|
// 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 *undefined_value = ir_build_const_undefined(irb, child_scope, elem_node);
|
|
ir_build_var_decl_src(irb, child_scope, elem_node, elem_var, elem_var_type, nullptr, undefined_value);
|
|
IrInstruction *elem_var_ptr = ir_build_var_ptr(irb, child_scope, node, elem_var);
|
|
|
|
AstNode *index_var_source_node;
|
|
ZigVar *index_var;
|
|
if (index_node) {
|
|
index_var_source_node = index_node;
|
|
Buf *index_var_name = index_node->data.symbol_expr.symbol;
|
|
index_var = ir_create_var(irb, index_node, child_scope, index_var_name, true, false, false, is_comptime);
|
|
} else {
|
|
index_var_source_node = node;
|
|
index_var = ir_create_var(irb, node, child_scope, nullptr, true, false, true, is_comptime);
|
|
}
|
|
child_scope = index_var->child_scope;
|
|
|
|
IrInstruction *usize = ir_build_const_type(irb, child_scope, node, irb->codegen->builtin_types.entry_usize);
|
|
IrInstruction *zero = ir_build_const_usize(irb, child_scope, node, 0);
|
|
IrInstruction *one = ir_build_const_usize(irb, child_scope, node, 1);
|
|
ir_build_var_decl_src(irb, child_scope, index_var_source_node, index_var, usize, nullptr, zero);
|
|
IrInstruction *index_ptr = ir_build_var_ptr(irb, child_scope, node, index_var);
|
|
|
|
|
|
IrBasicBlock *cond_block = ir_create_basic_block(irb, child_scope, "ForCond");
|
|
IrBasicBlock *body_block = ir_create_basic_block(irb, child_scope, "ForBody");
|
|
IrBasicBlock *end_block = ir_create_basic_block(irb, child_scope, "ForEnd");
|
|
IrBasicBlock *else_block = else_node ? ir_create_basic_block(irb, child_scope, "ForElse") : end_block;
|
|
IrBasicBlock *continue_block = ir_create_basic_block(irb, child_scope, "ForContinue");
|
|
|
|
Buf *len_field_name = buf_create_from_str("len");
|
|
IrInstruction *len_ref = ir_build_field_ptr(irb, child_scope, node, array_val_ptr, len_field_name);
|
|
IrInstruction *len_val = ir_build_load_ptr(irb, child_scope, node, len_ref);
|
|
ir_build_br(irb, child_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, child_scope, node, index_ptr);
|
|
IrInstruction *cond = ir_build_bin_op(irb, child_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));
|
|
ir_mark_gen(ir_build_cond_br(irb, child_scope, node, cond, body_block, else_block, is_comptime));
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, body_block);
|
|
IrInstruction *elem_ptr = ir_build_elem_ptr(irb, child_scope, node, array_val_ptr, index_val, false, PtrLenSingle);
|
|
IrInstruction *elem_val;
|
|
if (node->data.for_expr.elem_is_ptr) {
|
|
elem_val = elem_ptr;
|
|
} else {
|
|
elem_val = ir_build_load_ptr(irb, child_scope, node, elem_ptr);
|
|
}
|
|
ir_mark_gen(ir_build_store_ptr(irb, child_scope, node, elem_var_ptr, elem_val));
|
|
|
|
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;
|
|
|
|
IrInstruction *body_result = ir_gen_node(irb, body_node, &loop_scope->base);
|
|
|
|
if (!instr_is_unreachable(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_mark_gen(ir_build_store_ptr(irb, child_scope, node, index_ptr, new_index_val));
|
|
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);
|
|
|
|
else_result = ir_gen_node(irb, else_node, parent_scope);
|
|
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);
|
|
}
|
|
|
|
return ir_build_phi(irb, parent_scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
|
|
}
|
|
|
|
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_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;
|
|
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 (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);
|
|
}
|
|
}
|
|
|
|
static IrInstruction *ir_gen_promise_type(IrBuilder *irb, Scope *scope, AstNode *node) {
|
|
assert(node->type == NodeTypePromiseType);
|
|
|
|
AstNode *payload_type_node = node->data.promise_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_promise_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) {
|
|
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);
|
|
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);
|
|
}
|
|
ir_build_cond_br(irb, scope, node, is_non_null, then_block, else_block, 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) {
|
|
IrInstruction *var_type = nullptr;
|
|
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 *var_ptr_value = ir_build_optional_unwrap_ptr(irb, subexpr_scope, node, maybe_val_ptr, false);
|
|
IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, subexpr_scope, node, var_ptr_value);
|
|
ir_build_var_decl_src(irb, subexpr_scope, node, var, var_type, nullptr, var_value);
|
|
var_scope = var->child_scope;
|
|
} else {
|
|
var_scope = subexpr_scope;
|
|
}
|
|
IrInstruction *then_expr_result = ir_gen_node(irb, then_node, var_scope);
|
|
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(irb, else_node, subexpr_scope);
|
|
if (else_expr_result == irb->codegen->invalid_instruction)
|
|
return else_expr_result;
|
|
} else {
|
|
else_expr_result = ir_build_const_void(irb, scope, node);
|
|
}
|
|
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;
|
|
|
|
return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
|
|
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);
|
|
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(irb, scope, node, err_val);
|
|
|
|
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);
|
|
ir_build_cond_br(irb, scope, node, is_err, else_block, ok_block, 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) {
|
|
IrInstruction *var_type = nullptr;
|
|
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 *var_ptr_value = ir_build_unwrap_err_payload(irb, subexpr_scope, node, err_val_ptr, false);
|
|
IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, subexpr_scope, node, var_ptr_value);
|
|
ir_build_var_decl_src(irb, subexpr_scope, node, var, var_type, nullptr, var_value);
|
|
var_scope = var->child_scope;
|
|
} else {
|
|
var_scope = subexpr_scope;
|
|
}
|
|
IrInstruction *then_expr_result = ir_gen_node(irb, then_node, var_scope);
|
|
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) {
|
|
IrInstruction *var_type = nullptr;
|
|
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 *var_value = ir_build_unwrap_err_code(irb, subexpr_scope, node, err_val_ptr);
|
|
ir_build_var_decl_src(irb, subexpr_scope, node, var, var_type, nullptr, var_value);
|
|
err_var_scope = var->child_scope;
|
|
} else {
|
|
err_var_scope = subexpr_scope;
|
|
}
|
|
else_expr_result = ir_gen_node(irb, else_node, err_var_scope);
|
|
if (else_expr_result == irb->codegen->invalid_instruction)
|
|
return else_expr_result;
|
|
} else {
|
|
else_expr_result = ir_build_const_void(irb, scope, node);
|
|
}
|
|
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;
|
|
|
|
return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values);
|
|
}
|
|
|
|
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_value,
|
|
ZigList<IrBasicBlock *> *incoming_blocks, ZigList<IrInstruction *> *incoming_values)
|
|
{
|
|
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_value;
|
|
if (prong_value) {
|
|
IrInstruction *var_ptr_value = ir_build_switch_var(irb, scope, var_symbol_node, target_value_ptr, prong_value);
|
|
var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, scope, var_symbol_node, var_ptr_value);
|
|
} else {
|
|
var_value = var_is_ptr ? target_value_ptr : ir_build_load_ptr(irb, scope, var_symbol_node, target_value_ptr);
|
|
}
|
|
IrInstruction *var_type = nullptr; // infer the type
|
|
ir_build_var_decl_src(irb, scope, var_symbol_node, var, var_type, nullptr, var_value);
|
|
} else {
|
|
child_scope = scope;
|
|
}
|
|
|
|
IrInstruction *expr_result = ir_gen_node(irb, expr_node, child_scope);
|
|
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) {
|
|
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);
|
|
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};
|
|
|
|
// 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) {
|
|
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;
|
|
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, &incoming_blocks, &incoming_values))
|
|
{
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
ir_set_cursor_at_end(irb, prev_block);
|
|
} else if (prong_node->data.switch_prong.any_items_are_range) {
|
|
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);
|
|
ir_mark_gen(ir_build_cond_br(irb, scope, last_item_node, ok_bit, range_block_yes,
|
|
range_block_no, is_comptime));
|
|
|
|
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, &incoming_blocks, &incoming_values))
|
|
{
|
|
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;
|
|
|
|
IrBasicBlock *prong_block = ir_create_basic_block(irb, scope, "SwitchProng");
|
|
IrInstruction *last_item_value = 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);
|
|
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;
|
|
|
|
last_item_value = item_value;
|
|
}
|
|
IrInstruction *only_item_value = (prong_item_count == 1) ? last_item_value : nullptr;
|
|
|
|
IrBasicBlock *prev_block = irb->current_basic_block;
|
|
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, only_item_value, &incoming_blocks, &incoming_values))
|
|
{
|
|
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);
|
|
|
|
if (cases.length == 0) {
|
|
ir_build_br(irb, scope, node, else_block, is_comptime);
|
|
} else {
|
|
ir_build_switch_br(irb, scope, node, target_value, else_block, cases.length, cases.items, is_comptime, switch_prongs_void);
|
|
}
|
|
|
|
if (!else_prong) {
|
|
ir_set_cursor_at_end_and_append_block(irb, else_block);
|
|
ir_build_unreachable(irb, scope, node);
|
|
}
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, end_block);
|
|
assert(incoming_blocks.length == incoming_values.length);
|
|
if (incoming_blocks.length == 0) {
|
|
return ir_build_const_void(irb, scope, node);
|
|
} else {
|
|
return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
|
|
}
|
|
}
|
|
|
|
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);
|
|
return ir_gen_node_extra(irb, node->data.comptime_expr.expr, child_scope, lval);
|
|
}
|
|
|
|
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) {
|
|
result_value = ir_gen_node(irb, node->data.break_expr.expr, break_scope);
|
|
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) {
|
|
result_value = ir_gen_node(irb, node->data.break_expr.expr, break_scope);
|
|
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) {
|
|
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);
|
|
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;
|
|
}
|
|
|
|
return ir_build_slice(irb, scope, node, ptr_value, start_value, end_value, true);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
|
|
assert(node->type == NodeTypeUnwrapErrorExpr);
|
|
|
|
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, LValNone);
|
|
}
|
|
|
|
|
|
IrInstruction *err_union_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LValPtr);
|
|
if (err_union_ptr == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
IrInstruction *err_union_val = ir_build_load_ptr(irb, parent_scope, node, err_union_ptr);
|
|
IrInstruction *is_err = ir_build_test_err(irb, parent_scope, node, err_union_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_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");
|
|
ir_build_cond_br(irb, parent_scope, node, is_err, err_block, ok_block, is_comptime);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, err_block);
|
|
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, parent_scope, var_name,
|
|
is_const, is_const, is_shadowable, is_comptime);
|
|
err_scope = var->child_scope;
|
|
IrInstruction *err_val = ir_build_unwrap_err_code(irb, err_scope, node, err_union_ptr);
|
|
ir_build_var_decl_src(irb, err_scope, var_node, var, nullptr, nullptr, err_val);
|
|
} else {
|
|
err_scope = parent_scope;
|
|
}
|
|
IrInstruction *err_result = ir_gen_node(irb, op2_node, err_scope);
|
|
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, err_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);
|
|
IrInstruction *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr);
|
|
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;
|
|
return ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values);
|
|
}
|
|
|
|
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);
|
|
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->type_ref = g->builtin_types.entry_global_error_set->type_ref;
|
|
err_set_type->di_type = g->builtin_types.entry_global_error_set->di_type;
|
|
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, "}");
|
|
|
|
g->error_di_types.append(&err_set_type->di_type);
|
|
|
|
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->type_ref = g->builtin_types.entry_global_error_set->type_ref;
|
|
err_set_type->di_type = g->builtin_types.entry_global_error_set->di_type;
|
|
err_set_type->data.error_set.err_count = 1;
|
|
err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(1);
|
|
|
|
g->error_di_types.append(&err_set_type->di_type);
|
|
|
|
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->type_ref = irb->codegen->builtin_types.entry_global_error_set->type_ref;
|
|
err_set_type->di_type = irb->codegen->builtin_types.entry_global_error_set->di_type;
|
|
irb->codegen->error_di_types.append(&err_set_type->di_type);
|
|
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);
|
|
irb->codegen->err_enumerators.append(ZigLLVMCreateDebugEnumerator(irb->codegen->dbuilder,
|
|
buf_ptr(err_name), error_value_count));
|
|
}
|
|
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;
|
|
}
|
|
|
|
IrInstruction *async_allocator_type_value = nullptr;
|
|
if (node->data.fn_proto.async_allocator_type != nullptr) {
|
|
async_allocator_type_value = ir_gen_node(irb, node->data.fn_proto.async_allocator_type, parent_scope);
|
|
if (async_allocator_type_value == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
|
|
return ir_build_fn_proto(irb, parent_scope, node, param_types, align_value, return_type,
|
|
async_allocator_type_value, is_var_args);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode *node,
|
|
IrInstruction *target_inst, bool cancel_non_suspended, bool cancel_awaited)
|
|
{
|
|
IrBasicBlock *done_block = ir_create_basic_block(irb, scope, "CancelDone");
|
|
IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled");
|
|
IrBasicBlock *pre_return_block = ir_create_basic_block(irb, scope, "PreReturn");
|
|
IrBasicBlock *post_return_block = ir_create_basic_block(irb, scope, "PostReturn");
|
|
IrBasicBlock *do_cancel_block = ir_create_basic_block(irb, scope, "DoCancel");
|
|
|
|
IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
|
|
IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize);
|
|
IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false);
|
|
IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001
|
|
IrInstruction *promise_T_type_val = ir_build_const_type(irb, scope, node,
|
|
get_promise_type(irb->codegen, irb->codegen->builtin_types.entry_void));
|
|
IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111
|
|
IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000
|
|
IrInstruction *await_mask = ir_build_const_usize(irb, scope, node, 0x4); // 0b100
|
|
IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010
|
|
|
|
// TODO relies on Zig not re-ordering fields
|
|
IrInstruction *casted_target_inst = ir_build_ptr_cast_src(irb, scope, node, promise_T_type_val, target_inst,
|
|
false);
|
|
IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst);
|
|
Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME);
|
|
IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,
|
|
atomic_state_field_name);
|
|
|
|
// set the is_canceled bit
|
|
IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
|
|
usize_type_val, atomic_state_ptr, nullptr, is_canceled_mask, nullptr,
|
|
AtomicRmwOp_or, AtomicOrderSeqCst);
|
|
|
|
IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false);
|
|
IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false);
|
|
ir_build_cond_br(irb, scope, node, is_canceled_bool, done_block, not_canceled_block, is_comptime);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, not_canceled_block);
|
|
IrInstruction *awaiter_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false);
|
|
IrInstruction *is_returned_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpEq, awaiter_addr, ptr_mask, false);
|
|
ir_build_cond_br(irb, scope, node, is_returned_bool, post_return_block, pre_return_block, is_comptime);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, post_return_block);
|
|
if (cancel_awaited) {
|
|
ir_build_br(irb, scope, node, do_cancel_block, is_comptime);
|
|
} else {
|
|
IrInstruction *is_awaited_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, await_mask, false);
|
|
IrInstruction *is_awaited_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_awaited_value, zero, false);
|
|
ir_build_cond_br(irb, scope, node, is_awaited_bool, done_block, do_cancel_block, is_comptime);
|
|
}
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, pre_return_block);
|
|
if (cancel_awaited) {
|
|
if (cancel_non_suspended) {
|
|
ir_build_br(irb, scope, node, do_cancel_block, is_comptime);
|
|
} else {
|
|
IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false);
|
|
IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false);
|
|
ir_build_cond_br(irb, scope, node, is_suspended_bool, do_cancel_block, done_block, is_comptime);
|
|
}
|
|
} else {
|
|
ir_build_br(irb, scope, node, done_block, is_comptime);
|
|
}
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, do_cancel_block);
|
|
ir_build_cancel(irb, scope, node, target_inst);
|
|
ir_build_br(irb, scope, node, done_block, is_comptime);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, done_block);
|
|
return ir_build_const_void(irb, scope, node);
|
|
}
|
|
|
|
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;
|
|
|
|
return ir_gen_cancel_target(irb, scope, node, target_inst, false, true);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_resume_target(IrBuilder *irb, Scope *scope, AstNode *node,
|
|
IrInstruction *target_inst)
|
|
{
|
|
IrBasicBlock *done_block = ir_create_basic_block(irb, scope, "ResumeDone");
|
|
IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled");
|
|
IrBasicBlock *suspended_block = ir_create_basic_block(irb, scope, "IsSuspended");
|
|
IrBasicBlock *not_suspended_block = ir_create_basic_block(irb, scope, "IsNotSuspended");
|
|
|
|
IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
|
|
IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001
|
|
IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010
|
|
IrInstruction *and_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, is_suspended_mask);
|
|
IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false);
|
|
IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize);
|
|
IrInstruction *promise_T_type_val = ir_build_const_type(irb, scope, node,
|
|
get_promise_type(irb->codegen, irb->codegen->builtin_types.entry_void));
|
|
|
|
// TODO relies on Zig not re-ordering fields
|
|
IrInstruction *casted_target_inst = ir_build_ptr_cast_src(irb, scope, node, promise_T_type_val, target_inst,
|
|
false);
|
|
IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst);
|
|
Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME);
|
|
IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,
|
|
atomic_state_field_name);
|
|
|
|
// clear the is_suspended bit
|
|
IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
|
|
usize_type_val, atomic_state_ptr, nullptr, and_mask, nullptr,
|
|
AtomicRmwOp_and, AtomicOrderSeqCst);
|
|
|
|
IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false);
|
|
IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false);
|
|
ir_build_cond_br(irb, scope, node, is_canceled_bool, done_block, not_canceled_block, is_comptime);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, not_canceled_block);
|
|
IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false);
|
|
IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false);
|
|
ir_build_cond_br(irb, scope, node, is_suspended_bool, suspended_block, not_suspended_block, is_comptime);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, not_suspended_block);
|
|
ir_build_unreachable(irb, scope, node);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, suspended_block);
|
|
ir_build_coro_resume(irb, scope, node, target_inst);
|
|
ir_build_br(irb, scope, node, done_block, is_comptime);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, done_block);
|
|
return ir_build_const_void(irb, scope, node);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) {
|
|
assert(node->type == NodeTypeResume);
|
|
|
|
IrInstruction *target_inst = ir_gen_node(irb, node->data.resume_expr.expr, scope);
|
|
if (target_inst == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
return ir_gen_resume_target(irb, scope, node, target_inst);
|
|
}
|
|
|
|
static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
|
|
assert(node->type == NodeTypeAwaitExpr);
|
|
|
|
IrInstruction *target_inst = ir_gen_node(irb, node->data.await_expr.expr, scope);
|
|
if (target_inst == irb->codegen->invalid_instruction)
|
|
return irb->codegen->invalid_instruction;
|
|
|
|
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;
|
|
}
|
|
if (fn_entry->type_entry->data.fn.fn_type_id.cc != CallingConventionAsync) {
|
|
add_node_error(irb->codegen, node, buf_sprintf("await in non-async function"));
|
|
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 await inside defer expression"));
|
|
scope_defer_expr->reported_err = true;
|
|
}
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
|
|
Scope *outer_scope = irb->exec->begin_scope;
|
|
|
|
IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, target_inst);
|
|
Buf *result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME);
|
|
IrInstruction *result_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_ptr_field_name);
|
|
|
|
if (irb->codegen->have_err_ret_tracing) {
|
|
IrInstruction *err_ret_trace_ptr = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::NonNull);
|
|
Buf *err_ret_trace_ptr_field_name = buf_create_from_str(ERR_RET_TRACE_PTR_FIELD_NAME);
|
|
IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr_field_name);
|
|
ir_build_store_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr, err_ret_trace_ptr);
|
|
}
|
|
|
|
IrBasicBlock *already_awaited_block = ir_create_basic_block(irb, scope, "AlreadyAwaited");
|
|
IrBasicBlock *not_awaited_block = ir_create_basic_block(irb, scope, "NotAwaited");
|
|
IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled");
|
|
IrBasicBlock *yes_suspend_block = ir_create_basic_block(irb, scope, "YesSuspend");
|
|
IrBasicBlock *no_suspend_block = ir_create_basic_block(irb, scope, "NoSuspend");
|
|
IrBasicBlock *merge_block = ir_create_basic_block(irb, scope, "MergeSuspend");
|
|
IrBasicBlock *cleanup_block = ir_create_basic_block(irb, scope, "SuspendCleanup");
|
|
IrBasicBlock *resume_block = ir_create_basic_block(irb, scope, "SuspendResume");
|
|
IrBasicBlock *cancel_target_block = ir_create_basic_block(irb, scope, "CancelTarget");
|
|
IrBasicBlock *do_cancel_block = ir_create_basic_block(irb, scope, "DoCancel");
|
|
IrBasicBlock *do_defers_block = ir_create_basic_block(irb, scope, "DoDefers");
|
|
IrBasicBlock *destroy_block = ir_create_basic_block(irb, scope, "DestroyBlock");
|
|
IrBasicBlock *my_suspended_block = ir_create_basic_block(irb, scope, "AlreadySuspended");
|
|
IrBasicBlock *my_not_suspended_block = ir_create_basic_block(irb, scope, "NotAlreadySuspended");
|
|
IrBasicBlock *do_suspend_block = ir_create_basic_block(irb, scope, "DoSuspend");
|
|
|
|
Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME);
|
|
IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,
|
|
atomic_state_field_name);
|
|
|
|
IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_promise);
|
|
IrInstruction *const_bool_false = ir_build_const_bool(irb, scope, node, false);
|
|
IrInstruction *undefined_value = ir_build_const_undefined(irb, scope, node);
|
|
IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize);
|
|
IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
|
|
IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111
|
|
IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000
|
|
IrInstruction *await_mask = ir_build_const_usize(irb, scope, node, 0x4); // 0b100
|
|
IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001
|
|
IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010
|
|
|
|
ZigVar *result_var = ir_create_var(irb, node, scope, nullptr,
|
|
false, false, true, const_bool_false);
|
|
IrInstruction *target_promise_type = ir_build_typeof(irb, scope, node, target_inst);
|
|
IrInstruction *promise_result_type = ir_build_promise_result_type(irb, scope, node, target_promise_type);
|
|
ir_build_await_bookkeeping(irb, scope, node, promise_result_type);
|
|
ir_build_var_decl_src(irb, scope, node, result_var, promise_result_type, nullptr, undefined_value);
|
|
IrInstruction *my_result_var_ptr = ir_build_var_ptr(irb, scope, node, result_var);
|
|
ir_build_store_ptr(irb, scope, node, result_ptr_field_ptr, my_result_var_ptr);
|
|
IrInstruction *save_token = ir_build_coro_save(irb, scope, node, irb->exec->coro_handle);
|
|
|
|
IrInstruction *coro_handle_addr = ir_build_ptr_to_int(irb, scope, node, irb->exec->coro_handle);
|
|
IrInstruction *mask_bits = ir_build_bin_op(irb, scope, node, IrBinOpBinOr, coro_handle_addr, await_mask, false);
|
|
IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
|
|
usize_type_val, atomic_state_ptr, nullptr, mask_bits, nullptr,
|
|
AtomicRmwOp_or, AtomicOrderSeqCst);
|
|
|
|
IrInstruction *is_awaited_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, await_mask, false);
|
|
IrInstruction *is_awaited_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_awaited_value, zero, false);
|
|
ir_build_cond_br(irb, scope, node, is_awaited_bool, already_awaited_block, not_awaited_block, const_bool_false);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, already_awaited_block);
|
|
ir_build_unreachable(irb, scope, node);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, not_awaited_block);
|
|
IrInstruction *await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false);
|
|
IrInstruction *is_non_null = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false);
|
|
IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false);
|
|
IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false);
|
|
ir_build_cond_br(irb, scope, node, is_canceled_bool, cancel_target_block, not_canceled_block, const_bool_false);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, not_canceled_block);
|
|
ir_build_cond_br(irb, scope, node, is_non_null, no_suspend_block, yes_suspend_block, const_bool_false);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, cancel_target_block);
|
|
ir_build_cancel(irb, scope, node, target_inst);
|
|
ir_mark_gen(ir_build_br(irb, scope, node, cleanup_block, const_bool_false));
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, no_suspend_block);
|
|
if (irb->codegen->have_err_ret_tracing) {
|
|
Buf *err_ret_trace_field_name = buf_create_from_str(ERR_RET_TRACE_FIELD_NAME);
|
|
IrInstruction *src_err_ret_trace_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_field_name);
|
|
IrInstruction *dest_err_ret_trace_ptr = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::NonNull);
|
|
ir_build_merge_err_ret_traces(irb, scope, node, coro_promise_ptr, src_err_ret_trace_ptr, dest_err_ret_trace_ptr);
|
|
}
|
|
Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME);
|
|
IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name);
|
|
// If the type of the result handle_is_ptr then this does not actually perform a load. But we need it to,
|
|
// because we're about to destroy the memory. So we store it into our result variable.
|
|
IrInstruction *no_suspend_result = ir_build_load_ptr(irb, scope, node, promise_result_ptr);
|
|
ir_build_store_ptr(irb, scope, node, my_result_var_ptr, no_suspend_result);
|
|
ir_build_cancel(irb, scope, node, target_inst);
|
|
ir_build_br(irb, scope, node, merge_block, const_bool_false);
|
|
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, yes_suspend_block);
|
|
IrInstruction *my_prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
|
|
usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, is_suspended_mask, nullptr,
|
|
AtomicRmwOp_or, AtomicOrderSeqCst);
|
|
IrInstruction *my_is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, my_prev_atomic_value, is_suspended_mask, false);
|
|
IrInstruction *my_is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, my_is_suspended_value, zero, false);
|
|
ir_build_cond_br(irb, scope, node, my_is_suspended_bool, my_suspended_block, my_not_suspended_block, const_bool_false);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, my_suspended_block);
|
|
ir_build_unreachable(irb, scope, node);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, my_not_suspended_block);
|
|
IrInstruction *my_is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, my_prev_atomic_value, is_canceled_mask, false);
|
|
IrInstruction *my_is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, my_is_canceled_value, zero, false);
|
|
ir_build_cond_br(irb, scope, node, my_is_canceled_bool, cleanup_block, do_suspend_block, const_bool_false);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, do_suspend_block);
|
|
IrInstruction *suspend_code = ir_build_coro_suspend(irb, scope, node, save_token, const_bool_false);
|
|
|
|
IrInstructionSwitchBrCase *cases = allocate<IrInstructionSwitchBrCase>(2);
|
|
cases[0].value = ir_build_const_u8(irb, scope, node, 0);
|
|
cases[0].block = resume_block;
|
|
cases[1].value = ir_build_const_u8(irb, scope, node, 1);
|
|
cases[1].block = destroy_block;
|
|
ir_build_switch_br(irb, scope, node, suspend_code, irb->exec->coro_suspend_block,
|
|
2, cases, const_bool_false, nullptr);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, destroy_block);
|
|
ir_gen_cancel_target(irb, scope, node, target_inst, false, true);
|
|
ir_mark_gen(ir_build_br(irb, scope, node, cleanup_block, const_bool_false));
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, cleanup_block);
|
|
IrInstruction *my_mask_bits = ir_build_bin_op(irb, scope, node, IrBinOpBinOr, ptr_mask, is_canceled_mask, false);
|
|
IrInstruction *b_my_prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
|
|
usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, my_mask_bits, nullptr,
|
|
AtomicRmwOp_or, AtomicOrderSeqCst);
|
|
IrInstruction *my_await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, b_my_prev_atomic_value, ptr_mask, false);
|
|
IrInstruction *dont_have_my_await_handle = ir_build_bin_op(irb, scope, node, IrBinOpCmpEq, my_await_handle_addr, zero, false);
|
|
IrInstruction *dont_destroy_ourselves = ir_build_bin_op(irb, scope, node, IrBinOpBoolAnd, dont_have_my_await_handle, is_canceled_bool, false);
|
|
ir_build_cond_br(irb, scope, node, dont_have_my_await_handle, do_defers_block, do_cancel_block, const_bool_false);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, do_cancel_block);
|
|
IrInstruction *my_await_handle = ir_build_int_to_ptr(irb, scope, node, promise_type_val, my_await_handle_addr);
|
|
ir_gen_cancel_target(irb, scope, node, my_await_handle, true, false);
|
|
ir_mark_gen(ir_build_br(irb, scope, node, do_defers_block, const_bool_false));
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, do_defers_block);
|
|
ir_gen_defers_for_block(irb, scope, outer_scope, true);
|
|
ir_mark_gen(ir_build_cond_br(irb, scope, node, dont_destroy_ourselves, irb->exec->coro_early_final, irb->exec->coro_final_cleanup_block, const_bool_false));
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, resume_block);
|
|
ir_build_br(irb, scope, node, merge_block, const_bool_false);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, merge_block);
|
|
return ir_build_load_ptr(irb, scope, node, my_result_var_ptr);
|
|
}
|
|
|
|
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;
|
|
}
|
|
if (fn_entry->type_entry->data.fn.fn_type_id.cc != CallingConventionAsync) {
|
|
add_node_error(irb->codegen, node, buf_sprintf("suspend in non-async function"));
|
|
return irb->codegen->invalid_instruction;
|
|
}
|
|
|
|
ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(parent_scope);
|
|
if (scope_defer_expr) {
|
|
if (!scope_defer_expr->reported_err) {
|
|
ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot suspend inside defer expression"));
|
|
add_error_note(irb->codegen, msg, scope_defer_expr->base.source_node, buf_sprintf("defer here"));
|
|
scope_defer_expr->reported_err = true;
|
|
}
|
|
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;
|
|
}
|
|
|
|
Scope *outer_scope = irb->exec->begin_scope;
|
|
|
|
IrBasicBlock *cleanup_block = ir_create_basic_block(irb, parent_scope, "SuspendCleanup");
|
|
IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume");
|
|
IrBasicBlock *suspended_block = ir_create_basic_block(irb, parent_scope, "AlreadySuspended");
|
|
IrBasicBlock *canceled_block = ir_create_basic_block(irb, parent_scope, "IsCanceled");
|
|
IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, parent_scope, "NotCanceled");
|
|
IrBasicBlock *not_suspended_block = ir_create_basic_block(irb, parent_scope, "NotAlreadySuspended");
|
|
IrBasicBlock *cancel_awaiter_block = ir_create_basic_block(irb, parent_scope, "CancelAwaiter");
|
|
|
|
IrInstruction *promise_type_val = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_promise);
|
|
IrInstruction *const_bool_true = ir_build_const_bool(irb, parent_scope, node, true);
|
|
IrInstruction *const_bool_false = ir_build_const_bool(irb, parent_scope, node, false);
|
|
IrInstruction *usize_type_val = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_usize);
|
|
IrInstruction *is_canceled_mask = ir_build_const_usize(irb, parent_scope, node, 0x1); // 0b001
|
|
IrInstruction *is_suspended_mask = ir_build_const_usize(irb, parent_scope, node, 0x2); // 0b010
|
|
IrInstruction *zero = ir_build_const_usize(irb, parent_scope, node, 0);
|
|
IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, parent_scope, node, 0x7); // 0b111
|
|
IrInstruction *ptr_mask = ir_build_un_op(irb, parent_scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000
|
|
|
|
IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, parent_scope, node,
|
|
usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, is_suspended_mask, nullptr,
|
|
AtomicRmwOp_or, AtomicOrderSeqCst);
|
|
|
|
IrInstruction *is_canceled_value = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false);
|
|
IrInstruction *is_canceled_bool = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false);
|
|
ir_build_cond_br(irb, parent_scope, node, is_canceled_bool, canceled_block, not_canceled_block, const_bool_false);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, canceled_block);
|
|
IrInstruction *await_handle_addr = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false);
|
|
IrInstruction *have_await_handle = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false);
|
|
IrBasicBlock *post_canceled_block = irb->current_basic_block;
|
|
ir_build_cond_br(irb, parent_scope, node, have_await_handle, cancel_awaiter_block, cleanup_block, const_bool_false);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, cancel_awaiter_block);
|
|
IrInstruction *await_handle = ir_build_int_to_ptr(irb, parent_scope, node, promise_type_val, await_handle_addr);
|
|
ir_gen_cancel_target(irb, parent_scope, node, await_handle, true, false);
|
|
IrBasicBlock *post_cancel_awaiter_block = irb->current_basic_block;
|
|
ir_build_br(irb, parent_scope, node, cleanup_block, const_bool_false);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, not_canceled_block);
|
|
IrInstruction *is_suspended_value = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false);
|
|
IrInstruction *is_suspended_bool = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false);
|
|
ir_build_cond_br(irb, parent_scope, node, is_suspended_bool, suspended_block, not_suspended_block, const_bool_false);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, suspended_block);
|
|
ir_build_unreachable(irb, parent_scope, node);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, not_suspended_block);
|
|
IrInstruction *suspend_code;
|
|
if (node->data.suspend.block == nullptr) {
|
|
suspend_code = ir_build_coro_suspend(irb, parent_scope, node, nullptr, const_bool_false);
|
|
} else {
|
|
Scope *child_scope;
|
|
ScopeSuspend *suspend_scope = create_suspend_scope(irb->codegen, node, parent_scope);
|
|
suspend_scope->resume_block = resume_block;
|
|
child_scope = &suspend_scope->base;
|
|
IrInstruction *save_token = ir_build_coro_save(irb, child_scope, node, irb->exec->coro_handle);
|
|
ir_gen_node(irb, node->data.suspend.block, child_scope);
|
|
suspend_code = ir_mark_gen(ir_build_coro_suspend(irb, parent_scope, node, save_token, const_bool_false));
|
|
}
|
|
|
|
IrInstructionSwitchBrCase *cases = allocate<IrInstructionSwitchBrCase>(2);
|
|
cases[0].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 0));
|
|
cases[0].block = resume_block;
|
|
cases[1].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 1));
|
|
cases[1].block = canceled_block;
|
|
ir_mark_gen(ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block,
|
|
2, cases, const_bool_false, nullptr));
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, cleanup_block);
|
|
IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
|
|
IrInstruction **incoming_values = allocate<IrInstruction *>(2);
|
|
incoming_blocks[0] = post_canceled_block;
|
|
incoming_values[0] = const_bool_true;
|
|
incoming_blocks[1] = post_cancel_awaiter_block;
|
|
incoming_values[1] = const_bool_false;
|
|
IrInstruction *destroy_ourselves = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values);
|
|
ir_gen_defers_for_block(irb, parent_scope, outer_scope, true);
|
|
ir_mark_gen(ir_build_cond_br(irb, parent_scope, node, destroy_ourselves, irb->exec->coro_final_cleanup_block, irb->exec->coro_early_final, const_bool_false));
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, resume_block);
|
|
return ir_mark_gen(ir_build_const_void(irb, parent_scope, node));
|
|
}
|
|
|
|
static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope,
|
|
LVal lval)
|
|
{
|
|
assert(scope);
|
|
switch (node->type) {
|
|
case NodeTypeStructValueField:
|
|
case NodeTypeParamDecl:
|
|
case NodeTypeUse:
|
|
case NodeTypeSwitchProng:
|
|
case NodeTypeSwitchRange:
|
|
case NodeTypeStructField:
|
|
case NodeTypeFnDef:
|
|
case NodeTypeTestDecl:
|
|
zig_unreachable();
|
|
case NodeTypeBlock:
|
|
return ir_lval_wrap(irb, scope, ir_gen_block(irb, scope, node), lval);
|
|
case NodeTypeGroupedExpr:
|
|
return ir_gen_node_raw(irb, node->data.grouped_expr, scope, lval);
|
|
case NodeTypeBinOpExpr:
|
|
return ir_lval_wrap(irb, scope, ir_gen_bin_op(irb, scope, node), lval);
|
|
case NodeTypeIntLiteral:
|
|
return ir_lval_wrap(irb, scope, ir_gen_int_lit(irb, scope, node), lval);
|
|
case NodeTypeFloatLiteral:
|
|
return ir_lval_wrap(irb, scope, ir_gen_float_lit(irb, scope, node), lval);
|
|
case NodeTypeCharLiteral:
|
|
return ir_lval_wrap(irb, scope, ir_gen_char_lit(irb, scope, node), lval);
|
|
case NodeTypeSymbol:
|
|
return ir_gen_symbol(irb, scope, node, lval);
|
|
case NodeTypeFnCallExpr:
|
|
return ir_gen_fn_call(irb, scope, node, lval);
|
|
case NodeTypeIfBoolExpr:
|
|
return ir_lval_wrap(irb, scope, ir_gen_if_bool_expr(irb, scope, node), lval);
|
|
case NodeTypePrefixOpExpr:
|
|
return ir_gen_prefix_op_expr(irb, scope, node, lval);
|
|
case NodeTypeContainerInitExpr:
|
|
return ir_lval_wrap(irb, scope, ir_gen_container_init_expr(irb, scope, node), lval);
|
|
case NodeTypeVariableDeclaration:
|
|
return ir_lval_wrap(irb, scope, ir_gen_var_decl(irb, scope, node), lval);
|
|
case NodeTypeWhileExpr:
|
|
return ir_lval_wrap(irb, scope, ir_gen_while_expr(irb, scope, node), lval);
|
|
case NodeTypeForExpr:
|
|
return ir_lval_wrap(irb, scope, ir_gen_for_expr(irb, scope, node), lval);
|
|
case NodeTypeArrayAccessExpr:
|
|
return ir_gen_array_access(irb, scope, node, lval);
|
|
case NodeTypeReturnExpr:
|
|
return ir_gen_return(irb, scope, node, lval);
|
|
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;
|
|
|
|
return ir_build_load_ptr(irb, scope, node, ptr_instruction);
|
|
}
|
|
case NodeTypePtrDeref: {
|
|
AstNode *expr_node = node->data.ptr_deref_expr.target;
|
|
IrInstruction *value = ir_gen_node_extra(irb, expr_node, scope, lval);
|
|
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
|
|
return ir_build_un_op_lval(irb, scope, node, IrUnOpDereference, value, lval);
|
|
}
|
|
case NodeTypeUnwrapOptional: {
|
|
AstNode *expr_node = node->data.unwrap_optional.expr;
|
|
|
|
IrInstruction *maybe_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr);
|
|
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);
|
|
if (lval == LValPtr)
|
|
return unwrapped_ptr;
|
|
|
|
return ir_build_load_ptr(irb, scope, node, unwrapped_ptr);
|
|
}
|
|
case NodeTypeBoolLiteral:
|
|
return ir_lval_wrap(irb, scope, ir_gen_bool_literal(irb, scope, node), lval);
|
|
case NodeTypeArrayType:
|
|
return ir_lval_wrap(irb, scope, ir_gen_array_type(irb, scope, node), lval);
|
|
case NodeTypePointerType:
|
|
return ir_lval_wrap(irb, scope, ir_gen_pointer_type(irb, scope, node), lval);
|
|
case NodeTypePromiseType:
|
|
return ir_lval_wrap(irb, scope, ir_gen_promise_type(irb, scope, node), lval);
|
|
case NodeTypeStringLiteral:
|
|
return ir_lval_wrap(irb, scope, ir_gen_string_literal(irb, scope, node), lval);
|
|
case NodeTypeUndefinedLiteral:
|
|
return ir_lval_wrap(irb, scope, ir_gen_undefined_literal(irb, scope, node), lval);
|
|
case NodeTypeAsmExpr:
|
|
return ir_lval_wrap(irb, scope, ir_gen_asm_expr(irb, scope, node), lval);
|
|
case NodeTypeNullLiteral:
|
|
return ir_lval_wrap(irb, scope, ir_gen_null_literal(irb, scope, node), lval);
|
|
case NodeTypeIfErrorExpr:
|
|
return ir_lval_wrap(irb, scope, ir_gen_if_err_expr(irb, scope, node), lval);
|
|
case NodeTypeIfOptional:
|
|
return ir_lval_wrap(irb, scope, ir_gen_if_optional_expr(irb, scope, node), lval);
|
|
case NodeTypeSwitchExpr:
|
|
return ir_lval_wrap(irb, scope, ir_gen_switch_expr(irb, scope, node), lval);
|
|
case NodeTypeCompTime:
|
|
return ir_gen_comptime(irb, scope, node, lval);
|
|
case NodeTypeErrorType:
|
|
return ir_lval_wrap(irb, scope, ir_gen_error_type(irb, scope, node), lval);
|
|
case NodeTypeBreak:
|
|
return ir_lval_wrap(irb, scope, ir_gen_break(irb, scope, node), lval);
|
|
case NodeTypeContinue:
|
|
return ir_lval_wrap(irb, scope, ir_gen_continue(irb, scope, node), lval);
|
|
case NodeTypeUnreachable:
|
|
return ir_lval_wrap(irb, scope, ir_build_unreachable(irb, scope, node), lval);
|
|
case NodeTypeDefer:
|
|
return ir_lval_wrap(irb, scope, ir_gen_defer(irb, scope, node), lval);
|
|
case NodeTypeSliceExpr:
|
|
return ir_lval_wrap(irb, scope, ir_gen_slice(irb, scope, node), lval);
|
|
case NodeTypeUnwrapErrorExpr:
|
|
return ir_lval_wrap(irb, scope, ir_gen_catch(irb, scope, node), lval);
|
|
case NodeTypeContainerDecl:
|
|
return ir_lval_wrap(irb, scope, ir_gen_container_decl(irb, scope, node), lval);
|
|
case NodeTypeFnProto:
|
|
return ir_lval_wrap(irb, scope, ir_gen_fn_proto(irb, scope, node), lval);
|
|
case NodeTypeErrorSetDecl:
|
|
return ir_lval_wrap(irb, scope, ir_gen_err_set_decl(irb, scope, node), lval);
|
|
case NodeTypeCancel:
|
|
return ir_lval_wrap(irb, scope, ir_gen_cancel(irb, scope, node), lval);
|
|
case NodeTypeResume:
|
|
return ir_lval_wrap(irb, scope, ir_gen_resume(irb, scope, node), lval);
|
|
case NodeTypeAwaitExpr:
|
|
return ir_lval_wrap(irb, scope, ir_gen_await_expr(irb, scope, node), lval);
|
|
case NodeTypeSuspend:
|
|
return ir_lval_wrap(irb, scope, ir_gen_suspend(irb, scope, node), lval);
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval) {
|
|
IrInstruction *result = ir_gen_node_raw(irb, node, scope, lval);
|
|
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);
|
|
}
|
|
|
|
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);
|
|
|
|
ZigFn *fn_entry = exec_fn_entry(irb->exec);
|
|
|
|
bool is_async = fn_entry != nullptr && fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync;
|
|
IrInstruction *coro_id;
|
|
IrInstruction *u8_ptr_type;
|
|
IrInstruction *const_bool_false;
|
|
IrInstruction *coro_promise_ptr;
|
|
IrInstruction *err_ret_trace_ptr;
|
|
ZigType *return_type;
|
|
Buf *result_ptr_field_name;
|
|
ZigVar *coro_size_var;
|
|
if (is_async) {
|
|
// create the coro promise
|
|
Scope *coro_scope = create_coro_prelude_scope(irb->codegen, node, scope);
|
|
const_bool_false = ir_build_const_bool(irb, coro_scope, node, false);
|
|
ZigVar *promise_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false);
|
|
|
|
return_type = fn_entry->type_entry->data.fn.fn_type_id.return_type;
|
|
IrInstruction *undef = ir_build_const_undefined(irb, coro_scope, node);
|
|
ZigType *coro_frame_type = get_promise_frame_type(irb->codegen, return_type);
|
|
IrInstruction *coro_frame_type_value = ir_build_const_type(irb, coro_scope, node, coro_frame_type);
|
|
// TODO mark this var decl as "no safety" e.g. disable initializing the undef value to 0xaa
|
|
ir_build_var_decl_src(irb, coro_scope, node, promise_var, coro_frame_type_value, nullptr, undef);
|
|
coro_promise_ptr = ir_build_var_ptr(irb, coro_scope, node, promise_var);
|
|
|
|
ZigVar *await_handle_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false);
|
|
IrInstruction *null_value = ir_build_const_null(irb, coro_scope, node);
|
|
IrInstruction *await_handle_type_val = ir_build_const_type(irb, coro_scope, node,
|
|
get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise));
|
|
ir_build_var_decl_src(irb, coro_scope, node, await_handle_var, await_handle_type_val, nullptr, null_value);
|
|
irb->exec->await_handle_var_ptr = ir_build_var_ptr(irb, coro_scope, node, await_handle_var);
|
|
|
|
u8_ptr_type = ir_build_const_type(irb, coro_scope, node,
|
|
get_pointer_to_type(irb->codegen, irb->codegen->builtin_types.entry_u8, false));
|
|
IrInstruction *promise_as_u8_ptr = ir_build_ptr_cast_src(irb, coro_scope, node, u8_ptr_type,
|
|
coro_promise_ptr, false);
|
|
coro_id = ir_build_coro_id(irb, coro_scope, node, promise_as_u8_ptr);
|
|
coro_size_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false);
|
|
IrInstruction *coro_size = ir_build_coro_size(irb, coro_scope, node);
|
|
ir_build_var_decl_src(irb, coro_scope, node, coro_size_var, nullptr, nullptr, coro_size);
|
|
IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, coro_scope, node,
|
|
ImplicitAllocatorIdArg);
|
|
irb->exec->coro_allocator_var = ir_create_var(irb, node, coro_scope, nullptr, true, true, true, const_bool_false);
|
|
ir_build_var_decl_src(irb, coro_scope, node, irb->exec->coro_allocator_var, nullptr, nullptr, implicit_allocator_ptr);
|
|
Buf *realloc_field_name = buf_create_from_str(ASYNC_REALLOC_FIELD_NAME);
|
|
IrInstruction *realloc_fn_ptr = ir_build_field_ptr(irb, coro_scope, node, implicit_allocator_ptr, realloc_field_name);
|
|
IrInstruction *realloc_fn = ir_build_load_ptr(irb, coro_scope, node, realloc_fn_ptr);
|
|
IrInstruction *maybe_coro_mem_ptr = ir_build_coro_alloc_helper(irb, coro_scope, node, realloc_fn, coro_size);
|
|
IrInstruction *alloc_result_is_ok = ir_build_test_nonnull(irb, coro_scope, node, maybe_coro_mem_ptr);
|
|
IrBasicBlock *alloc_err_block = ir_create_basic_block(irb, coro_scope, "AllocError");
|
|
IrBasicBlock *alloc_ok_block = ir_create_basic_block(irb, coro_scope, "AllocOk");
|
|
ir_build_cond_br(irb, coro_scope, node, alloc_result_is_ok, alloc_ok_block, alloc_err_block, const_bool_false);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, alloc_err_block);
|
|
// we can return undefined here, because the caller passes a pointer to the error struct field
|
|
// in the error union result, and we populate it in case of allocation failure.
|
|
ir_build_return(irb, coro_scope, node, undef);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, alloc_ok_block);
|
|
IrInstruction *coro_mem_ptr = ir_build_ptr_cast_src(irb, coro_scope, node, u8_ptr_type, maybe_coro_mem_ptr,
|
|
false);
|
|
irb->exec->coro_handle = ir_build_coro_begin(irb, coro_scope, node, coro_id, coro_mem_ptr);
|
|
|
|
Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME);
|
|
irb->exec->atomic_state_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,
|
|
atomic_state_field_name);
|
|
IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
|
|
ir_build_store_ptr(irb, scope, node, irb->exec->atomic_state_field_ptr, zero);
|
|
Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME);
|
|
irb->exec->coro_result_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name);
|
|
result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME);
|
|
irb->exec->coro_result_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_ptr_field_name);
|
|
ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr, irb->exec->coro_result_field_ptr);
|
|
if (irb->codegen->have_err_ret_tracing) {
|
|
// initialize the error return trace
|
|
Buf *return_addresses_field_name = buf_create_from_str(RETURN_ADDRESSES_FIELD_NAME);
|
|
IrInstruction *return_addresses_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, return_addresses_field_name);
|
|
|
|
Buf *err_ret_trace_field_name = buf_create_from_str(ERR_RET_TRACE_FIELD_NAME);
|
|
err_ret_trace_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_field_name);
|
|
ir_build_mark_err_ret_trace_ptr(irb, scope, node, err_ret_trace_ptr);
|
|
|
|
// coordinate with builtin.zig
|
|
Buf *index_name = buf_create_from_str("index");
|
|
IrInstruction *index_ptr = ir_build_field_ptr(irb, scope, node, err_ret_trace_ptr, index_name);
|
|
ir_build_store_ptr(irb, scope, node, index_ptr, zero);
|
|
|
|
Buf *instruction_addresses_name = buf_create_from_str("instruction_addresses");
|
|
IrInstruction *addrs_slice_ptr = ir_build_field_ptr(irb, scope, node, err_ret_trace_ptr, instruction_addresses_name);
|
|
|
|
IrInstruction *slice_value = ir_build_slice(irb, scope, node, return_addresses_ptr, zero, nullptr, false);
|
|
ir_build_store_ptr(irb, scope, node, addrs_slice_ptr, slice_value);
|
|
}
|
|
|
|
|
|
irb->exec->coro_early_final = ir_create_basic_block(irb, scope, "CoroEarlyFinal");
|
|
irb->exec->coro_normal_final = ir_create_basic_block(irb, scope, "CoroNormalFinal");
|
|
irb->exec->coro_suspend_block = ir_create_basic_block(irb, scope, "Suspend");
|
|
irb->exec->coro_final_cleanup_block = ir_create_basic_block(irb, scope, "FinalCleanup");
|
|
}
|
|
|
|
IrInstruction *result = ir_gen_node_extra(irb, node, scope, LValNone);
|
|
assert(result);
|
|
if (irb->exec->invalid)
|
|
return false;
|
|
|
|
if (!instr_is_unreachable(result)) {
|
|
// no need for save_err_ret_addr because this cannot return error
|
|
ir_gen_async_return(irb, scope, result->source_node, result, true);
|
|
}
|
|
|
|
if (is_async) {
|
|
IrBasicBlock *invalid_resume_block = ir_create_basic_block(irb, scope, "InvalidResume");
|
|
IrBasicBlock *check_free_block = ir_create_basic_block(irb, scope, "CheckFree");
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_early_final);
|
|
IrInstruction *const_bool_true = ir_build_const_bool(irb, scope, node, true);
|
|
IrInstruction *suspend_code = ir_build_coro_suspend(irb, scope, node, nullptr, const_bool_true);
|
|
IrInstructionSwitchBrCase *cases = allocate<IrInstructionSwitchBrCase>(2);
|
|
cases[0].value = ir_build_const_u8(irb, scope, node, 0);
|
|
cases[0].block = invalid_resume_block;
|
|
cases[1].value = ir_build_const_u8(irb, scope, node, 1);
|
|
cases[1].block = irb->exec->coro_final_cleanup_block;
|
|
ir_build_switch_br(irb, scope, node, suspend_code, irb->exec->coro_suspend_block, 2, cases, const_bool_false, nullptr);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_suspend_block);
|
|
ir_build_coro_end(irb, scope, node);
|
|
ir_build_return(irb, scope, node, irb->exec->coro_handle);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, invalid_resume_block);
|
|
ir_build_unreachable(irb, scope, node);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_normal_final);
|
|
if (type_has_bits(return_type)) {
|
|
IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node,
|
|
get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8,
|
|
false, false, PtrLenUnknown, 0, 0, 0));
|
|
IrInstruction *result_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr);
|
|
IrInstruction *result_ptr_as_u8_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len,
|
|
result_ptr, false);
|
|
IrInstruction *return_value_ptr_as_u8_ptr = ir_build_ptr_cast_src(irb, scope, node,
|
|
u8_ptr_type_unknown_len, irb->exec->coro_result_field_ptr, false);
|
|
IrInstruction *return_type_inst = ir_build_const_type(irb, scope, node,
|
|
fn_entry->type_entry->data.fn.fn_type_id.return_type);
|
|
IrInstruction *size_of_ret_val = ir_build_size_of(irb, scope, node, return_type_inst);
|
|
ir_build_memcpy(irb, scope, node, result_ptr_as_u8_ptr, return_value_ptr_as_u8_ptr, size_of_ret_val);
|
|
}
|
|
if (irb->codegen->have_err_ret_tracing) {
|
|
Buf *err_ret_trace_ptr_field_name = buf_create_from_str(ERR_RET_TRACE_PTR_FIELD_NAME);
|
|
IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr_field_name);
|
|
IrInstruction *dest_err_ret_trace_ptr = ir_build_load_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr);
|
|
ir_build_merge_err_ret_traces(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr, dest_err_ret_trace_ptr);
|
|
}
|
|
// Before we destroy the coroutine frame, we need to load the target promise into
|
|
// a register or local variable which does not get spilled into the frame,
|
|
// otherwise llvm tries to access memory inside the destroyed frame.
|
|
IrInstruction *unwrapped_await_handle_ptr = ir_build_optional_unwrap_ptr(irb, scope, node,
|
|
irb->exec->await_handle_var_ptr, false);
|
|
IrInstruction *await_handle_in_block = ir_build_load_ptr(irb, scope, node, unwrapped_await_handle_ptr);
|
|
ir_build_br(irb, scope, node, check_free_block, const_bool_false);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_final_cleanup_block);
|
|
ir_build_br(irb, scope, node, check_free_block, const_bool_false);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, check_free_block);
|
|
IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
|
|
IrInstruction **incoming_values = allocate<IrInstruction *>(2);
|
|
incoming_blocks[0] = irb->exec->coro_final_cleanup_block;
|
|
incoming_values[0] = const_bool_false;
|
|
incoming_blocks[1] = irb->exec->coro_normal_final;
|
|
incoming_values[1] = const_bool_true;
|
|
IrInstruction *resume_awaiter = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values);
|
|
|
|
IrBasicBlock **merge_incoming_blocks = allocate<IrBasicBlock *>(2);
|
|
IrInstruction **merge_incoming_values = allocate<IrInstruction *>(2);
|
|
merge_incoming_blocks[0] = irb->exec->coro_final_cleanup_block;
|
|
merge_incoming_values[0] = ir_build_const_undefined(irb, scope, node);
|
|
merge_incoming_blocks[1] = irb->exec->coro_normal_final;
|
|
merge_incoming_values[1] = await_handle_in_block;
|
|
IrInstruction *awaiter_handle = ir_build_phi(irb, scope, node, 2, merge_incoming_blocks, merge_incoming_values);
|
|
|
|
Buf *shrink_field_name = buf_create_from_str(ASYNC_SHRINK_FIELD_NAME);
|
|
IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, scope, node,
|
|
ImplicitAllocatorIdLocalVar);
|
|
IrInstruction *shrink_fn_ptr = ir_build_field_ptr(irb, scope, node, implicit_allocator_ptr, shrink_field_name);
|
|
IrInstruction *shrink_fn = ir_build_load_ptr(irb, scope, node, shrink_fn_ptr);
|
|
IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
|
|
IrInstruction *coro_mem_ptr_maybe = ir_build_coro_free(irb, scope, node, coro_id, irb->exec->coro_handle);
|
|
IrInstruction *u8_ptr_type_unknown_len = ir_build_const_type(irb, scope, node,
|
|
get_pointer_to_type_extra(irb->codegen, irb->codegen->builtin_types.entry_u8,
|
|
false, false, PtrLenUnknown, 0, 0, 0));
|
|
IrInstruction *coro_mem_ptr = ir_build_ptr_cast_src(irb, scope, node, u8_ptr_type_unknown_len,
|
|
coro_mem_ptr_maybe, false);
|
|
IrInstruction *coro_mem_ptr_ref = ir_build_ref(irb, scope, node, coro_mem_ptr, true, false);
|
|
IrInstruction *coro_size_ptr = ir_build_var_ptr(irb, scope, node, coro_size_var);
|
|
IrInstruction *coro_size = ir_build_load_ptr(irb, scope, node, coro_size_ptr);
|
|
IrInstruction *mem_slice = ir_build_slice(irb, scope, node, coro_mem_ptr_ref, zero, coro_size, false);
|
|
size_t arg_count = 5;
|
|
IrInstruction **args = allocate<IrInstruction *>(arg_count);
|
|
args[0] = implicit_allocator_ptr; // self
|
|
args[1] = mem_slice; // old_mem
|
|
args[2] = ir_build_const_usize(irb, scope, node, 8); // old_align
|
|
// TODO: intentional memory leak here. If this is set to 0 then there is an issue where a coroutine
|
|
// calls the function and it frees its own stack frame, but then the return value is a slice, which
|
|
// is implemented as an sret struct. writing to the return pointer causes invalid memory write.
|
|
// We could work around it by having a global helper function which has a void return type
|
|
// and calling that instead. But instead this hack will suffice until I rework coroutines to be
|
|
// non-allocating. Basically coroutines are not supported right now until they are reworked.
|
|
args[3] = ir_build_const_usize(irb, scope, node, 1); // new_size
|
|
args[4] = ir_build_const_usize(irb, scope, node, 1); // new_align
|
|
ir_build_call(irb, scope, node, nullptr, shrink_fn, arg_count, args, false, FnInlineAuto, false, nullptr, nullptr);
|
|
|
|
IrBasicBlock *resume_block = ir_create_basic_block(irb, scope, "Resume");
|
|
ir_build_cond_br(irb, scope, node, resume_awaiter, resume_block, irb->exec->coro_suspend_block, const_bool_false);
|
|
|
|
ir_set_cursor_at_end_and_append_block(irb, resume_block);
|
|
ir_gen_resume_target(irb, scope, node, awaiter_handle);
|
|
ir_build_br(irb, scope, node, irb->exec->coro_suspend_block, const_bool_false);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
// 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 *value = ret_inst->value;
|
|
if (value->value.special == ConstValSpecialRuntime) {
|
|
exec_add_error_node(codegen, exec, value->source_node,
|
|
buf_sprintf("unable to evaluate constant expression"));
|
|
return &codegen->invalid_instruction->value;
|
|
}
|
|
return &value->value;
|
|
} else if (ir_has_side_effects(instruction)) {
|
|
exec_add_error_node(codegen, exec, instruction->source_node,
|
|
buf_sprintf("unable to evaluate constant expression"));
|
|
return &codegen->invalid_instruction->value;
|
|
}
|
|
}
|
|
return &codegen->invalid_instruction->value;
|
|
}
|
|
|
|
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 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 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_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;
|
|
}
|
|
|
|
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);
|
|
for (uint32_t i = 0; i < set1->data.error_set.err_count; i += 1) {
|
|
ErrorTableEntry *error_entry = set1->data.error_set.errors[i];
|
|
assert(errors[error_entry->value] == nullptr);
|
|
errors[error_entry->value] = error_entry;
|
|
}
|
|
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->type_ref = ira->codegen->builtin_types.entry_global_error_set->type_ref;
|
|
err_set_type->di_type = ira->codegen->builtin_types.entry_global_error_set->di_type;
|
|
err_set_type->data.error_set.err_count = intersection_list.length;
|
|
err_set_type->data.error_set.errors = intersection_list.items;
|
|
err_set_type->zero_bits = intersection_list.length == 0;
|
|
|
|
buf_appendf(&err_set_type->name, "}");
|
|
|
|
ira->codegen->error_di_types.append(&err_set_type->di_type);
|
|
|
|
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;
|
|
}
|
|
|
|
if (wanted_type == ira->codegen->builtin_types.entry_promise &&
|
|
actual_type->id == ZigTypeIdPromise)
|
|
{
|
|
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.is_generic && wanted_type->data.fn.fn_type_id.cc == CallingConventionAsync) {
|
|
ConstCastOnly child = types_match_const_cast_only(ira,
|
|
actual_type->data.fn.fn_type_id.async_allocator_type,
|
|
wanted_type->data.fn.fn_type_id.async_allocator_type,
|
|
source_node, false);
|
|
if (child.id == ConstCastResultIdInvalid)
|
|
return child;
|
|
if (child.id != ConstCastResultIdOk) {
|
|
result.id = ConstCastResultIdAsyncAllocatorType;
|
|
result.data.async_allocator_type = allocate_nonzero<ConstCastOnly>(1);
|
|
*result.data.async_allocator_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 = instructions[0];
|
|
if (type_is_invalid(prev_inst->value.type)) {
|
|
return ira->codegen->builtin_types.entry_invalid;
|
|
}
|
|
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 (size_t i = 1; 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) {
|
|
assert(err_set_type != nullptr);
|
|
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;
|
|
continue;
|
|
}
|
|
|
|
if (cur_type->id == ZigTypeIdNull) {
|
|
any_are_null = true;
|
|
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 (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);
|
|
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 {
|
|
return get_optional_type(ira->codegen, prev_inst->value.type);
|
|
}
|
|
} else {
|
|
return prev_inst->value.type;
|
|
}
|
|
}
|
|
|
|
static void ir_add_alloca(IrAnalyze *ira, IrInstruction *instruction, ZigType *type_entry) {
|
|
if (type_has_bits(type_entry) && handle_is_ptr(type_entry)) {
|
|
ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec);
|
|
if (fn_entry != nullptr) {
|
|
fn_entry->alloca_list.append(instruction);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_global_refs) {
|
|
ConstGlobalRefs *global_refs = dest->global_refs;
|
|
*dest = *src;
|
|
if (!same_global_refs) {
|
|
dest->global_refs = global_refs;
|
|
if (dest->type->id == ZigTypeIdStruct) {
|
|
dest->data.x_struct.fields = allocate_nonzero<ConstExprValue>(dest->type->data.structure.src_field_count);
|
|
memcpy(dest->data.x_struct.fields, src->data.x_struct.fields, sizeof(ConstExprValue) * dest->type->data.structure.src_field_count);
|
|
}
|
|
}
|
|
}
|
|
|
|
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:
|
|
case CastOpPtrOfArrayToSlice:
|
|
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 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 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, bool need_alloca)
|
|
{
|
|
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;
|
|
if (need_alloca) {
|
|
ir_add_alloca(ira, result, 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)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node,
|
|
wanted_type, value, CastOpPtrOfArrayToSlice);
|
|
result->value.type = wanted_type;
|
|
ir_add_alloca(ira, result, wanted_type);
|
|
return result;
|
|
}
|
|
|
|
static bool is_container(ZigType *type) {
|
|
return type->id == ZigTypeIdStruct ||
|
|
type->id == ZigTypeIdEnum ||
|
|
type->id == ZigTypeIdUnion;
|
|
}
|
|
|
|
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) {
|
|
ira->instruction_index = 0;
|
|
ira->old_irb.current_basic_block = old_bb;
|
|
ira->const_predecessor_bb = const_predecessor_bb;
|
|
}
|
|
|
|
static void ir_finish_bb(IrAnalyze *ira) {
|
|
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;
|
|
}
|
|
|
|
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) {
|
|
ira->old_bb_index += 1;
|
|
continue;
|
|
}
|
|
if (old_bb->other->instruction_list.length != 0) {
|
|
ira->old_bb_index += 1;
|
|
continue;
|
|
}
|
|
ira->new_irb.current_basic_block = old_bb->other;
|
|
|
|
ir_start_bb(ira, old_bb, nullptr);
|
|
return;
|
|
}
|
|
if (!need_repeat)
|
|
return;
|
|
need_repeat = false;
|
|
ira->old_bb_index = 0;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
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) {
|
|
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_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);
|
|
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(codegen, 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 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_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)
|
|
{
|
|
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;
|
|
}
|
|
|
|
IrInstruction *result = ir_build_maybe_wrap(&ira->new_irb, source_instr->scope, source_instr->source_node, value);
|
|
result->value.type = wanted_type;
|
|
result->value.data.rh_maybe = RuntimeHintOptionalNonNull;
|
|
ir_add_alloca(ira, result, wanted_type);
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction *source_instr,
|
|
IrInstruction *value, ZigType *wanted_type)
|
|
{
|
|
assert(wanted_type->id == ZigTypeIdErrorUnion);
|
|
|
|
if (instr_is_comptime(value)) {
|
|
ZigType *payload_type = wanted_type->data.error_union.payload_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, UndefBad);
|
|
if (!val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ConstExprValue *err_set_val = create_const_vals(1);
|
|
err_set_val->type = wanted_type->data.error_union.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 = ir_build_err_wrap_payload(&ira->new_irb, source_instr->scope, source_instr->source_node, value);
|
|
result->value.type = wanted_type;
|
|
result->value.data.rh_error_union = RuntimeHintErrorUnionNonError;
|
|
ir_add_alloca(ira, result, wanted_type);
|
|
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_err_wrap_code(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, ZigType *wanted_type) {
|
|
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 = ir_build_err_wrap_code(&ira->new_irb, source_instr->scope, source_instr->source_node, value);
|
|
result->value.type = wanted_type;
|
|
result->value.data.rh_error_union = RuntimeHintErrorUnionError;
|
|
ir_add_alloca(ira, result, wanted_type);
|
|
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);
|
|
|
|
IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb, source_instr->scope, source_instr->source_node);
|
|
const_instruction->base.value.special = ConstValSpecialStatic;
|
|
if (get_codegen_ptr_type(wanted_type) != nullptr) {
|
|
const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialNull;
|
|
} else if (is_opt_err_set(wanted_type)) {
|
|
const_instruction->base.value.data.x_err_set = nullptr;
|
|
} else {
|
|
const_instruction->base.value.data.x_optional = nullptr;
|
|
}
|
|
const_instruction->base.value.type = wanted_type;
|
|
return &const_instruction->base;
|
|
}
|
|
|
|
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);
|
|
IrInstruction *new_instruction = ir_build_ref(&ira->new_irb, source_instruction->scope,
|
|
source_instruction->source_node, value, is_const, is_volatile);
|
|
new_instruction->value.type = ptr_type;
|
|
new_instruction->value.data.rh_ptr = RuntimeHintPtrStack;
|
|
if (type_has_bits(ptr_type) && !handle_is_ptr(value->value.type)) {
|
|
ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec);
|
|
assert(fn_entry);
|
|
fn_entry->alloca_list.append(new_instruction);
|
|
}
|
|
return new_instruction;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_array_to_slice(IrAnalyze *ira, IrInstruction *source_instr,
|
|
IrInstruction *array_arg, ZigType *wanted_type)
|
|
{
|
|
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);
|
|
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);
|
|
|
|
IrInstruction *result = ir_build_slice(&ira->new_irb, source_instr->scope,
|
|
source_instr->source_node, array_ptr, start, end, false);
|
|
result->value.type = wanted_type;
|
|
result->value.data.rh_slice.id = RuntimeHintSliceIdLen;
|
|
result->value.data.rh_slice.len = array_type->data.array.len;
|
|
ir_add_alloca(ira, result, result->value.type);
|
|
|
|
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 (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;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
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 *target, ZigType *wanted_type)
|
|
{
|
|
Error err;
|
|
assert(wanted_type->id == ZigTypeIdUnion);
|
|
assert(target->value.type->id == ZigTypeIdEnum);
|
|
|
|
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;
|
|
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)
|
|
{
|
|
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;
|
|
}
|
|
return ir_build_vector_to_array(ira, source_instr, vector, array_type);
|
|
}
|
|
|
|
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 IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr,
|
|
ZigType *wanted_type, IrInstruction *value)
|
|
{
|
|
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, false);
|
|
}
|
|
|
|
// 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);
|
|
} 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);
|
|
} 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
} 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);
|
|
} 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);
|
|
if (type_is_invalid(cast1->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
|
|
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);
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
if (type_is_invalid(cast1->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
|
|
if (type_is_invalid(cast2->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
return cast2;
|
|
}
|
|
}
|
|
|
|
// *[N]T to [*]T
|
|
if (wanted_type->id == ZigTypeIdPointer &&
|
|
wanted_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_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);
|
|
}
|
|
}
|
|
|
|
|
|
// 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 [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);
|
|
if (type_is_invalid(cast1->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
|
|
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);
|
|
}
|
|
|
|
// 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 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
|
|
if (wanted_type->id == ZigTypeIdUnion && actual_type->id == ZigTypeIdEnum &&
|
|
(wanted_type->data.unionation.decl_node->data.container_decl.auto_enum ||
|
|
wanted_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))
|
|
{
|
|
if ((err = type_resolve(ira->codegen, wanted_type, ResolveStatusZeroBitsKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (wanted_type->data.unionation.tag_type == actual_type) {
|
|
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)
|
|
{
|
|
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 (actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.child_type->id != ZigTypeIdPointer) {
|
|
ZigType *dest_ptr_type = nullptr;
|
|
if (wanted_type->id == ZigTypeIdPointer &&
|
|
wanted_type->data.pointer.ptr_len == PtrLenSingle &&
|
|
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.ptr_len == PtrLenSingle &&
|
|
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 &&
|
|
(!actual_type->data.pointer.is_const || dest_ptr_type->data.pointer.is_const) &&
|
|
(!actual_type->data.pointer.is_volatile || dest_ptr_type->data.pointer.is_volatile) &&
|
|
actual_type->data.pointer.bit_offset_in_host == dest_ptr_type->data.pointer.bit_offset_in_host &&
|
|
actual_type->data.pointer.host_int_bytes == dest_ptr_type->data.pointer.host_int_bytes &&
|
|
get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, dest_ptr_type))
|
|
{
|
|
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);
|
|
}
|
|
|
|
// 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(IrAnalyze *ira, IrInstruction *value, ZigType *expected_type) {
|
|
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);
|
|
}
|
|
|
|
static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruction, IrInstruction *ptr) {
|
|
Error err;
|
|
ZigType *type_entry = ptr->value.type;
|
|
if (type_is_invalid(type_entry)) {
|
|
return ira->codegen->invalid_instruction;
|
|
} else if (type_entry->id == ZigTypeIdPointer) {
|
|
ZigType *child_type = type_entry->data.pointer.child_type;
|
|
// dereferencing a *u0 is comptime known to be 0
|
|
if (child_type->id == ZigTypeIdInt && child_type->data.integral.bit_count == 0) {
|
|
IrInstruction *result = ir_const(ira, source_instruction, child_type);
|
|
init_const_unsigned_negative(&result->value, child_type, 0, false);
|
|
return result;
|
|
}
|
|
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 == ConstPtrMutComptimeConst ||
|
|
ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar)
|
|
{
|
|
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 = ir_build_load_ptr_gen(ira, source_instruction, ptr, child_type);
|
|
if (type_entry->data.pointer.host_int_bytes != 0 && handle_is_ptr(child_type)) {
|
|
ir_add_alloca(ira, result, child_type);
|
|
}
|
|
return result;
|
|
} else {
|
|
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;
|
|
}
|
|
}
|
|
|
|
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);
|
|
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(IrAnalyze *ira, IrInstructionReturn *instruction) {
|
|
IrInstruction *value = instruction->value->child;
|
|
if (type_is_invalid(value->value.type))
|
|
return ir_unreach_error(ira);
|
|
|
|
IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->explicit_return_type);
|
|
if (type_is_invalid(casted_value->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);
|
|
}
|
|
|
|
if (casted_value->value.special == ConstValSpecialRuntime &&
|
|
casted_value->value.type->id == ZigTypeIdPointer &&
|
|
casted_value->value.data.rh_ptr == RuntimeHintPtrStack)
|
|
{
|
|
ir_add_error(ira, casted_value, 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_value);
|
|
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 == ZigTypeIdOptional) ||
|
|
(op2->value.type->id == ZigTypeIdNull && op1->value.type->id == ZigTypeIdOptional) ||
|
|
(op1->value.type->id == ZigTypeIdNull && op2->value.type->id == ZigTypeIdNull)))
|
|
{
|
|
if (op1->value.type->id == ZigTypeIdNull && op2->value.type->id == ZigTypeIdNull) {
|
|
return ir_const_bool(ira, &bin_op_instruction->base, (op_id == IrBinOpCmpEq));
|
|
}
|
|
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 (op1->value.type->id == ZigTypeIdNull || op2->value.type->id == ZigTypeIdNull) {
|
|
ir_add_error_node(ira, source_node, buf_sprintf("comparison against null can only be done with optionals"));
|
|
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 ZigTypeIdPromise:
|
|
case ZigTypeIdEnum:
|
|
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:
|
|
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) {
|
|
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);
|
|
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);
|
|
|
|
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) {
|
|
out_array_val->data.x_array.data.s_none.elements[next_index] = op1_array_val->data.x_array.data.s_none.elements[i];
|
|
}
|
|
for (size_t i = op2_array_index; i < op2_array_end; i += 1, next_index += 1) {
|
|
out_array_val->data.x_array.data.s_none.elements[next_index] = op2_array_val->data.x_array.data.s_none.elements[i];
|
|
}
|
|
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) {
|
|
out_val->data.x_array.data.s_none.elements[i] = array_val->data.x_array.data.s_none.elements[y];
|
|
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;
|
|
|
|
IrInstruction *init_value = decl_var_instruction->init_value->child;
|
|
if (type_is_invalid(init_value->value.type)) {
|
|
var->var_type = ira->codegen->builtin_types.entry_invalid;
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
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;
|
|
|
|
IrInstruction *casted_init_value = ir_implicit_cast(ira, init_value, explicit_type);
|
|
bool is_comptime_var = ir_get_var_is_comptime(var);
|
|
|
|
bool var_class_requires_const = false;
|
|
|
|
ZigType *result_type = casted_init_value->value.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) {
|
|
ir_add_error_node(ira, source_node,
|
|
buf_sprintf("variable of type '%s' not allowed", buf_ptr(&result_type->name)));
|
|
result_type = ira->codegen->builtin_types.entry_invalid;
|
|
}
|
|
|
|
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 (casted_init_value->value.special == ConstValSpecialStatic &&
|
|
casted_init_value->value.type->id == ZigTypeIdFn &&
|
|
casted_init_value->value.data.x_ptr.special != ConstPtrSpecialHardCodedAddr &&
|
|
casted_init_value->value.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 = casted_init_value->value.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 (casted_init_value->value.special != ConstValSpecialRuntime) {
|
|
if (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, &casted_init_value->value, !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, casted_init_value);
|
|
}
|
|
|
|
static VarLinkage global_linkage_to_var_linkage(GlobalLinkageId id) {
|
|
switch (id) {
|
|
case GlobalLinkageIdStrong:
|
|
return VarLinkageExportStrong;
|
|
case GlobalLinkageIdWeak:
|
|
return VarLinkageExportWeak;
|
|
case GlobalLinkageIdLinkOnce:
|
|
return VarLinkageExportLinkOnce;
|
|
case GlobalLinkageIdInternal:
|
|
return VarLinkageInternal;
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
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 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 ZigTypeIdUndefined:
|
|
case ZigTypeIdNull:
|
|
case ZigTypeIdOptional:
|
|
case ZigTypeIdErrorUnion:
|
|
case ZigTypeIdErrorSet:
|
|
case ZigTypeIdBoundFn:
|
|
case ZigTypeIdArgTuple:
|
|
case ZigTypeIdOpaque:
|
|
case ZigTypeIdPromise:
|
|
ir_add_error(ira, target,
|
|
buf_sprintf("invalid export target '%s'", buf_ptr(&type_value->name)));
|
|
break;
|
|
}
|
|
} break;
|
|
case ZigTypeIdVoid:
|
|
case ZigTypeIdBool:
|
|
case ZigTypeIdInt:
|
|
case ZigTypeIdFloat:
|
|
case ZigTypeIdPointer:
|
|
case ZigTypeIdArray:
|
|
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 ZigTypeIdPromise:
|
|
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;
|
|
var->linkage = global_linkage_to_var_linkage(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);
|
|
}
|
|
|
|
IrInstruction *ir_get_implicit_allocator(IrAnalyze *ira, IrInstruction *source_instr, ImplicitAllocatorId id) {
|
|
ZigFn *parent_fn_entry = exec_fn_entry(ira->new_irb.exec);
|
|
if (parent_fn_entry == nullptr) {
|
|
ir_add_error(ira, source_instr, buf_sprintf("no implicit allocator available"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
FnTypeId *parent_fn_type = &parent_fn_entry->type_entry->data.fn.fn_type_id;
|
|
if (parent_fn_type->cc != CallingConventionAsync) {
|
|
ir_add_error(ira, source_instr, buf_sprintf("async function call from non-async caller requires allocator parameter"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
assert(parent_fn_type->async_allocator_type != nullptr);
|
|
|
|
switch (id) {
|
|
case ImplicitAllocatorIdArg:
|
|
{
|
|
IrInstruction *result = ir_build_get_implicit_allocator(&ira->new_irb, source_instr->scope,
|
|
source_instr->source_node, ImplicitAllocatorIdArg);
|
|
result->value.type = parent_fn_type->async_allocator_type;
|
|
return result;
|
|
}
|
|
case ImplicitAllocatorIdLocalVar:
|
|
{
|
|
ZigVar *coro_allocator_var = ira->old_irb.exec->coro_allocator_var;
|
|
assert(coro_allocator_var != nullptr);
|
|
IrInstruction *var_ptr_inst = ir_get_var_ptr(ira, source_instr, coro_allocator_var);
|
|
IrInstruction *result = ir_get_deref(ira, source_instr, var_ptr_inst);
|
|
assert(result->value.type != nullptr);
|
|
return result;
|
|
}
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCall *call_instruction, ZigFn *fn_entry, ZigType *fn_type,
|
|
IrInstruction *fn_ref, IrInstruction **casted_args, size_t arg_count, IrInstruction *async_allocator_inst)
|
|
{
|
|
Buf *realloc_field_name = buf_create_from_str(ASYNC_REALLOC_FIELD_NAME);
|
|
assert(async_allocator_inst->value.type->id == ZigTypeIdPointer);
|
|
ZigType *container_type = async_allocator_inst->value.type->data.pointer.child_type;
|
|
IrInstruction *field_ptr_inst = ir_analyze_container_field_ptr(ira, realloc_field_name, &call_instruction->base,
|
|
async_allocator_inst, container_type);
|
|
if (type_is_invalid(field_ptr_inst->value.type)) {
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
ZigType *ptr_to_realloc_fn_type = field_ptr_inst->value.type;
|
|
assert(ptr_to_realloc_fn_type->id == ZigTypeIdPointer);
|
|
|
|
ZigType *realloc_fn_type = ptr_to_realloc_fn_type->data.pointer.child_type;
|
|
if (realloc_fn_type->id != ZigTypeIdFn) {
|
|
ir_add_error(ira, &call_instruction->base,
|
|
buf_sprintf("expected reallocation function, found '%s'", buf_ptr(&realloc_fn_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
ZigType *realloc_fn_return_type = realloc_fn_type->data.fn.fn_type_id.return_type;
|
|
if (realloc_fn_return_type->id != ZigTypeIdErrorUnion) {
|
|
ir_add_error(ira, fn_ref,
|
|
buf_sprintf("expected allocation function to return error union, but it returns '%s'", buf_ptr(&realloc_fn_return_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
ZigType *alloc_fn_error_set_type = realloc_fn_return_type->data.error_union.err_set_type;
|
|
ZigType *return_type = fn_type->data.fn.fn_type_id.return_type;
|
|
ZigType *promise_type = get_promise_type(ira->codegen, return_type);
|
|
ZigType *async_return_type = get_error_union_type(ira->codegen, alloc_fn_error_set_type, promise_type);
|
|
|
|
IrInstruction *result = ir_build_call(&ira->new_irb, call_instruction->base.scope, call_instruction->base.source_node,
|
|
fn_entry, fn_ref, arg_count, casted_args, false, FnInlineAuto, true, async_allocator_inst, nullptr);
|
|
result->value.type = async_return_type;
|
|
return result;
|
|
}
|
|
|
|
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 = analyze_type_expr(ira->codegen, *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, UndefBad);
|
|
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 = analyze_type_expr(ira->codegen, *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) {
|
|
generic_id->params[generic_id->param_count] = *arg_val;
|
|
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) {
|
|
size_t next_var_i = 0;
|
|
FnGenParamInfo *gen_param_info = fn_entry->type_entry->data.fn.gen_param_info;
|
|
assert(gen_param_info != nullptr);
|
|
for (size_t param_i = 0; param_i < index; param_i += 1) {
|
|
FnGenParamInfo *info = &gen_param_info[param_i];
|
|
if (info->gen_index == SIZE_MAX)
|
|
continue;
|
|
|
|
next_var_i += 1;
|
|
}
|
|
FnGenParamInfo *info = &gen_param_info[index];
|
|
if (info->gen_index == SIZE_MAX)
|
|
return nullptr;
|
|
|
|
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->linkage == VarLinkageExternal;
|
|
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);
|
|
|
|
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)
|
|
{
|
|
assert(ptr->value.type->id == ZigTypeIdPointer);
|
|
|
|
if (ptr->value.data.x_ptr.special == ConstPtrSpecialDiscard) {
|
|
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 && !source_instr->is_gen) {
|
|
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) {
|
|
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 (!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);
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
IrInstruction *result = ir_build_store_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node,
|
|
ptr, value);
|
|
result->value.type = ira->codegen->builtin_types.entry_void;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *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->cc == CallingConventionAsync && !call_instruction->is_async) {
|
|
ErrorMsg *msg = ir_add_error(ira, fn_ref, buf_sprintf("must use async keyword to call async function"));
|
|
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->cc != CallingConventionAsync && call_instruction->is_async) {
|
|
ErrorMsg *msg = ir_add_error(ira, fn_ref, buf_sprintf("cannot use async keyword to call non-async function"));
|
|
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);
|
|
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 = analyze_type_expr(ira->codegen, 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);
|
|
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);
|
|
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);
|
|
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);
|
|
const_instruction->base.value = *align_result;
|
|
|
|
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 = analyze_type_expr(ira->codegen, 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;
|
|
}
|
|
}
|
|
IrInstruction *async_allocator_inst = nullptr;
|
|
if (call_instruction->is_async) {
|
|
AstNode *async_allocator_type_node = fn_proto_node->data.fn_proto.async_allocator_type;
|
|
if (async_allocator_type_node != nullptr) {
|
|
ZigType *async_allocator_type = analyze_type_expr(ira->codegen, impl_fn->child_scope, async_allocator_type_node);
|
|
if (type_is_invalid(async_allocator_type))
|
|
return ira->codegen->invalid_instruction;
|
|
inst_fn_type_id.async_allocator_type = async_allocator_type;
|
|
}
|
|
IrInstruction *uncasted_async_allocator_inst;
|
|
if (call_instruction->async_allocator == nullptr) {
|
|
uncasted_async_allocator_inst = ir_get_implicit_allocator(ira, &call_instruction->base,
|
|
ImplicitAllocatorIdLocalVar);
|
|
if (type_is_invalid(uncasted_async_allocator_inst->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
} else {
|
|
uncasted_async_allocator_inst = call_instruction->async_allocator->child;
|
|
if (type_is_invalid(uncasted_async_allocator_inst->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
if (inst_fn_type_id.async_allocator_type == nullptr) {
|
|
inst_fn_type_id.async_allocator_type = uncasted_async_allocator_inst->value.type;
|
|
}
|
|
async_allocator_inst = ir_implicit_cast(ira, uncasted_async_allocator_inst, inst_fn_type_id.async_allocator_type);
|
|
if (type_is_invalid(async_allocator_inst->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
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;
|
|
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,
|
|
fn_ref, casted_args, impl_param_count, async_allocator_inst);
|
|
ir_add_alloca(ira, result, result->value.type);
|
|
return ir_finish_anal(ira, result);
|
|
}
|
|
|
|
assert(async_allocator_inst == nullptr);
|
|
IrInstruction *new_call_instruction = ir_build_call(&ira->new_irb,
|
|
call_instruction->base.scope, call_instruction->base.source_node,
|
|
impl_fn, nullptr, impl_param_count, casted_args, false, fn_inline,
|
|
call_instruction->is_async, nullptr, casted_new_stack);
|
|
new_call_instruction->value.type = impl_fn_type_id->return_type;
|
|
|
|
ir_add_alloca(ira, new_call_instruction, impl_fn_type_id->return_type);
|
|
|
|
return ir_finish_anal(ira, new_call_instruction);
|
|
}
|
|
|
|
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);
|
|
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 (call_instruction->is_async) {
|
|
IrInstruction *uncasted_async_allocator_inst;
|
|
if (call_instruction->async_allocator == nullptr) {
|
|
uncasted_async_allocator_inst = ir_get_implicit_allocator(ira, &call_instruction->base,
|
|
ImplicitAllocatorIdLocalVar);
|
|
if (type_is_invalid(uncasted_async_allocator_inst->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
} else {
|
|
uncasted_async_allocator_inst = call_instruction->async_allocator->child;
|
|
if (type_is_invalid(uncasted_async_allocator_inst->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
}
|
|
IrInstruction *async_allocator_inst = ir_implicit_cast(ira, uncasted_async_allocator_inst, fn_type_id->async_allocator_type);
|
|
if (type_is_invalid(async_allocator_inst->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *result = ir_analyze_async_call(ira, call_instruction, fn_entry, fn_type, fn_ref,
|
|
casted_args, call_param_count, async_allocator_inst);
|
|
ir_add_alloca(ira, result, result->value.type);
|
|
return ir_finish_anal(ira, result);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
IrInstruction *new_call_instruction = ir_build_call(&ira->new_irb,
|
|
call_instruction->base.scope, call_instruction->base.source_node,
|
|
fn_entry, fn_ref, call_param_count, casted_args, false, fn_inline, false, nullptr, casted_new_stack);
|
|
new_call_instruction->value.type = return_type;
|
|
ir_add_alloca(ira, new_call_instruction, return_type);
|
|
return ir_finish_anal(ira, new_call_instruction);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_call(IrAnalyze *ira, IrInstructionCall *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);
|
|
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 == ConstPtrMutComptimeConst);
|
|
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_maybe(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 = ensure_complete_type(ira->codegen, type_entry)))
|
|
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 ZigTypeIdUndefined:
|
|
case ZigTypeIdNull:
|
|
case ZigTypeIdOptional:
|
|
case ZigTypeIdErrorUnion:
|
|
case ZigTypeIdErrorSet:
|
|
case ZigTypeIdEnum:
|
|
case ZigTypeIdUnion:
|
|
case ZigTypeIdFn:
|
|
case ZigTypeIdBoundFn:
|
|
case ZigTypeIdArgTuple:
|
|
case ZigTypeIdPromise:
|
|
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);
|
|
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_maybe(ira, instruction);
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
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)
|
|
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);
|
|
|
|
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)
|
|
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);
|
|
|
|
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);
|
|
|
|
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();
|
|
}
|
|
|
|
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);
|
|
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 (casted_value == ira->codegen->invalid_instruction) {
|
|
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);
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
} 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);
|
|
}
|
|
} 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 != 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];
|
|
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);
|
|
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 = 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);
|
|
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_container_field_ptr(IrAnalyze *ira, Buf *field_name,
|
|
IrInstruction *source_instr, IrInstruction *container_ptr, ZigType *container_type)
|
|
{
|
|
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);
|
|
bool is_const = container_ptr->value.type->data.pointer.is_const;
|
|
bool is_volatile = container_ptr->value.type->data.pointer.is_volatile;
|
|
if (bare_type->id == ZigTypeIdStruct) {
|
|
TypeStructField *field = find_struct_type_field(bare_type, field_name);
|
|
if (field) {
|
|
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;
|
|
}
|
|
bool is_packed = (bare_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 = container_ptr->value.type->data.pointer.bit_offset_in_host;
|
|
uint32_t ptr_host_int_bytes = container_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, bare_type, field) : ptr_host_int_bytes;
|
|
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 *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;
|
|
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);
|
|
IrInstruction *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 = container_ptr->value.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,
|
|
container_ptr, field);
|
|
result->value.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),
|
|
host_int_bytes_for_result_type);
|
|
return result;
|
|
} else {
|
|
return ir_analyze_container_member_access_inner(ira, bare_type, field_name,
|
|
source_instr, container_ptr, container_type);
|
|
}
|
|
} else if (bare_type->id == ZigTypeIdEnum) {
|
|
return ir_analyze_container_member_access_inner(ira, bare_type, field_name,
|
|
source_instr, container_ptr, container_type);
|
|
} else if (bare_type->id == ZigTypeIdUnion) {
|
|
TypeUnionField *field = find_union_type_field(bare_type, field_name);
|
|
if (field) {
|
|
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;
|
|
|
|
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;
|
|
|
|
ZigType *field_type = field->type_entry;
|
|
ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field_type,
|
|
is_const, is_volatile, PtrLenSingle, 0, 0, 0);
|
|
|
|
IrInstruction *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);
|
|
result->value.type = get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, is_volatile,
|
|
PtrLenSingle, 0, 0, 0);
|
|
return result;
|
|
} else {
|
|
return ir_analyze_container_member_access_inner(ira, bare_type, field_name,
|
|
source_instr, container_ptr, container_type);
|
|
}
|
|
} else {
|
|
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 && !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_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:
|
|
zig_unreachable();
|
|
case TldIdVar:
|
|
{
|
|
TldVar *tld_var = (TldVar *)tld;
|
|
ZigVar *var = tld_var->var;
|
|
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;
|
|
|
|
// TODO instead of allocating this every time, put it in the tld value and we can reference
|
|
// the same one every time
|
|
ConstExprValue *const_val = create_const_vals(1);
|
|
const_val->special = ConstValSpecialStatic;
|
|
const_val->type = fn_entry->type_entry;
|
|
const_val->data.x_ptr.data.fn.fn_entry = fn_entry;
|
|
const_val->data.x_ptr.special = ConstPtrSpecialFunction;
|
|
const_val->data.x_ptr.mut = ConstPtrMutComptimeConst;
|
|
|
|
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);
|
|
}
|
|
|
|
bool ptr_is_const = true;
|
|
bool ptr_is_volatile = false;
|
|
return ir_get_const_ptr(ira, source_instruction, const_val, fn_entry->type_entry,
|
|
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
|
|
}
|
|
}
|
|
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_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);
|
|
IrInstruction *result = ir_analyze_container_field_ptr(ira, field_name, &field_ptr_instruction->base, container_child, bare_type);
|
|
return result;
|
|
} else {
|
|
IrInstruction *result = ir_analyze_container_field_ptr(ira, field_name, &field_ptr_instruction->base, container_ptr, container_type);
|
|
return result;
|
|
}
|
|
} else if (is_array_ref(container_type)) {
|
|
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 (is_slice(child_type) && buf_eql_str(field_name, "Child")) {
|
|
bool ptr_is_const = true;
|
|
bool ptr_is_volatile = false;
|
|
TypeStructField *ptr_field = &child_type->data.structure.fields[slice_ptr_index];
|
|
assert(ptr_field->type_entry->id == ZigTypeIdPointer);
|
|
ZigType *child_type = ptr_field->type_entry->data.pointer.child_type;
|
|
return ir_get_const_ptr(ira, &field_ptr_instruction->base,
|
|
create_const_type(ira->codegen, child_type),
|
|
ira->codegen->builtin_types.entry_type,
|
|
ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0);
|
|
}
|
|
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->err_enumerators.append(ZigLLVMCreateDebugEnumerator(ira->codegen->dbuilder,
|
|
buf_ptr(field_name), error_value_count));
|
|
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 {
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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_to_ptr_type(IrAnalyze *ira,
|
|
IrInstructionToPtrType *to_ptr_type_instruction)
|
|
{
|
|
Error err;
|
|
|
|
IrInstruction *value = to_ptr_type_instruction->value->child;
|
|
ZigType *type_entry = value->value.type;
|
|
if (type_is_invalid(type_entry))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *ptr_type;
|
|
if (type_entry->id == ZigTypeIdArray) {
|
|
// TODO: Allow capturing pointer to const array.
|
|
// const a = "123"; for (a) |*c| continue;
|
|
// error: expected type '*u8', found '*const u8'
|
|
ptr_type = get_pointer_to_type(ira->codegen, type_entry->data.array.child_type, false);
|
|
} else if (is_array_ref(type_entry)) {
|
|
ptr_type = get_pointer_to_type(ira->codegen,
|
|
type_entry->data.pointer.child_type->data.array.child_type, type_entry->data.pointer.is_const);
|
|
} else if (is_slice(type_entry)) {
|
|
ZigType *slice_ptr_type = type_entry->data.structure.fields[0].type_entry;
|
|
ptr_type = adjust_ptr_len(ira->codegen, slice_ptr_type, PtrLenSingle);
|
|
// If the pointer is over-aligned, we may have to reduce it based on the alignment of the element type.
|
|
if (slice_ptr_type->data.pointer.explicit_alignment != 0) {
|
|
ZigType *elem_type = slice_ptr_type->data.pointer.child_type;
|
|
if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusAlignmentKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
uint32_t elem_align = get_abi_alignment(ira->codegen, elem_type);
|
|
uint32_t reduced_align = min(elem_align, slice_ptr_type->data.pointer.explicit_alignment);
|
|
ptr_type = adjust_ptr_align(ira->codegen, ptr_type, reduced_align);
|
|
}
|
|
} else if (type_entry->id == ZigTypeIdArgTuple) {
|
|
ConstExprValue *arg_tuple_val = ir_resolve_const(ira, value, UndefBad);
|
|
if (!arg_tuple_val)
|
|
return ira->codegen->invalid_instruction;
|
|
zig_panic("TODO for loop on var args");
|
|
} else {
|
|
ir_add_error_node(ira, to_ptr_type_instruction->base.source_node,
|
|
buf_sprintf("expected array type, found '%s'", buf_ptr(&type_entry->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
return ir_const_type(ira, &to_ptr_type_instruction->base, ptr_type);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_ptr_type_child(IrAnalyze *ira,
|
|
IrInstructionPtrTypeChild *ptr_type_child_instruction)
|
|
{
|
|
IrInstruction *type_value = ptr_type_child_instruction->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->id != ZigTypeIdPointer) {
|
|
ir_add_error_node(ira, ptr_type_child_instruction->base.source_node,
|
|
buf_sprintf("expected pointer type, found '%s'", buf_ptr(&type_entry->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
return ir_const_type(ira, &ptr_type_child_instruction->base, type_entry->data.pointer.child_type);
|
|
}
|
|
|
|
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_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;
|
|
|
|
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 ZigTypeIdOptional:
|
|
case ZigTypeIdErrorUnion:
|
|
case ZigTypeIdErrorSet:
|
|
case ZigTypeIdEnum:
|
|
case ZigTypeIdUnion:
|
|
case ZigTypeIdFn:
|
|
case ZigTypeIdBoundFn:
|
|
case ZigTypeIdPromise:
|
|
case ZigTypeIdVector:
|
|
{
|
|
if ((err = type_resolve(ira->codegen, child_type, ResolveStatusZeroBitsKnown)))
|
|
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);
|
|
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 ZigTypeIdOptional:
|
|
case ZigTypeIdErrorUnion:
|
|
case ZigTypeIdErrorSet:
|
|
case ZigTypeIdEnum:
|
|
case ZigTypeIdUnion:
|
|
case ZigTypeIdFn:
|
|
case ZigTypeIdBoundFn:
|
|
case ZigTypeIdPromise:
|
|
case ZigTypeIdVector:
|
|
{
|
|
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_promise_type(IrAnalyze *ira, IrInstructionPromiseType *instruction) {
|
|
ZigType *promise_type;
|
|
|
|
if (instruction->payload_type == nullptr) {
|
|
promise_type = ira->codegen->builtin_types.entry_promise;
|
|
} else {
|
|
ZigType *payload_type = ir_resolve_type(ira, instruction->payload_type->child);
|
|
if (type_is_invalid(payload_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
promise_type = get_promise_type(ira->codegen, payload_type);
|
|
}
|
|
|
|
return ir_const_type(ira, &instruction->base, promise_type);
|
|
}
|
|
|
|
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 ZigTypeIdComptimeFloat:
|
|
case ZigTypeIdComptimeInt:
|
|
case ZigTypeIdBoundFn:
|
|
case ZigTypeIdMetaType:
|
|
case ZigTypeIdArgTuple:
|
|
case ZigTypeIdOpaque:
|
|
ir_add_error_node(ira, size_of_instruction->base.source_node,
|
|
buf_sprintf("no size available for type '%s'", buf_ptr(&type_entry->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
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 ZigTypeIdPromise:
|
|
case ZigTypeIdVector:
|
|
{
|
|
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 == ZigTypeIdOptional) {
|
|
if (instr_is_comptime(value)) {
|
|
ConstExprValue *maybe_val = ir_resolve_const(ira, value, UndefBad);
|
|
if (!maybe_val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
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)
|
|
{
|
|
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 != 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);
|
|
|
|
if (instr_is_comptime(base_ptr)) {
|
|
ConstExprValue *val = ir_resolve_const(ira, base_ptr, UndefBad);
|
|
if (!val)
|
|
return ira->codegen->invalid_instruction;
|
|
ConstExprValue *maybe_val = const_ptr_pointee(ira, ira->codegen, val, source_instr->source_node);
|
|
if (maybe_val == nullptr)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (val->data.x_ptr.mut != ConstPtrMutRuntimeVar) {
|
|
if (optional_value_is_null(maybe_val)) {
|
|
ir_add_error(ira, source_instr, buf_sprintf("unable to unwrap null"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
IrInstruction *result = ir_const(ira, source_instr, result_type);
|
|
ConstExprValue *out_val = &result->value;
|
|
out_val->data.x_ptr.special = ConstPtrSpecialRef;
|
|
out_val->data.x_ptr.mut = val->data.x_ptr.mut;
|
|
if (types_have_same_zig_comptime_repr(type_entry, child_type)) {
|
|
out_val->data.x_ptr.data.ref.pointee = maybe_val;
|
|
} else {
|
|
out_val->data.x_ptr.data.ref.pointee = maybe_val->data.x_optional;
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
IrInstruction *result = ir_build_optional_unwrap_ptr(&ira->new_irb, source_instr->scope,
|
|
source_instr->source_node, base_ptr, safety_check_on);
|
|
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);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_ctz(IrAnalyze *ira, IrInstructionCtz *ctz_instruction) {
|
|
IrInstruction *value = ctz_instruction->value->child;
|
|
if (type_is_invalid(value->value.type)) {
|
|
return ira->codegen->invalid_instruction;
|
|
} else if (value->value.type->id == ZigTypeIdInt) {
|
|
ZigType *return_type = get_smallest_unsigned_int_type(ira->codegen,
|
|
value->value.type->data.integral.bit_count);
|
|
if (value->value.special != ConstValSpecialRuntime) {
|
|
size_t result_usize = bigint_ctz(&value->value.data.x_bigint,
|
|
value->value.type->data.integral.bit_count);
|
|
IrInstruction *result = ir_const(ira, &ctz_instruction->base, return_type);
|
|
bigint_init_unsigned(&result->value.data.x_bigint, result_usize);
|
|
return result;
|
|
}
|
|
|
|
IrInstruction *result = ir_build_ctz(&ira->new_irb,
|
|
ctz_instruction->base.scope, ctz_instruction->base.source_node, value);
|
|
result->value.type = return_type;
|
|
return result;
|
|
} else {
|
|
ir_add_error_node(ira, ctz_instruction->base.source_node,
|
|
buf_sprintf("expected integer type, found '%s'", buf_ptr(&value->value.type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_clz(IrAnalyze *ira, IrInstructionClz *clz_instruction) {
|
|
IrInstruction *value = clz_instruction->value->child;
|
|
if (type_is_invalid(value->value.type)) {
|
|
return ira->codegen->invalid_instruction;
|
|
} else if (value->value.type->id == ZigTypeIdInt) {
|
|
ZigType *return_type = get_smallest_unsigned_int_type(ira->codegen,
|
|
value->value.type->data.integral.bit_count);
|
|
if (value->value.special != ConstValSpecialRuntime) {
|
|
size_t result_usize = bigint_clz(&value->value.data.x_bigint,
|
|
value->value.type->data.integral.bit_count);
|
|
IrInstruction *result = ir_const(ira, &clz_instruction->base, return_type);
|
|
bigint_init_unsigned(&result->value.data.x_bigint, result_usize);
|
|
return result;
|
|
}
|
|
|
|
IrInstruction *result = ir_build_clz(&ira->new_irb,
|
|
clz_instruction->base.scope, clz_instruction->base.source_node, value);
|
|
result->value.type = return_type;
|
|
return result;
|
|
} else {
|
|
ir_add_error_node(ira, clz_instruction->base.source_node,
|
|
buf_sprintf("expected integer type, found '%s'", buf_ptr(&value->value.type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_pop_count(IrAnalyze *ira, IrInstructionPopCount *instruction) {
|
|
IrInstruction *value = instruction->value->child;
|
|
if (type_is_invalid(value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (value->value.type->id != ZigTypeIdInt && value->value.type->id != ZigTypeIdComptimeInt) {
|
|
ir_add_error(ira, value,
|
|
buf_sprintf("expected integer type, found '%s'", buf_ptr(&value->value.type->name)));
|
|
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;
|
|
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);
|
|
}
|
|
if (value->value.type->id == ZigTypeIdComptimeInt) {
|
|
Buf *val_buf = buf_alloc();
|
|
bigint_append_buf(val_buf, &val->data.x_bigint, 10);
|
|
ir_add_error(ira, &instruction->base,
|
|
buf_sprintf("@popCount on negative %s value %s",
|
|
buf_ptr(&value->value.type->name), buf_ptr(val_buf)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
size_t result = bigint_popcount_signed(&val->data.x_bigint, value->value.type->data.integral.bit_count);
|
|
return ir_const_unsigned(ira, &instruction->base, result);
|
|
}
|
|
|
|
IrInstruction *result = ir_build_pop_count(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node, value);
|
|
result->value.type = get_smallest_unsigned_int_type(ira->codegen, value->value.type->data.integral.bit_count);
|
|
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);
|
|
IrInstruction *result = 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);
|
|
result->value.type = ira->codegen->builtin_types.entry_unreachable;
|
|
return ir_finish_anal(ira, result);
|
|
}
|
|
|
|
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 ZigTypeIdPointer:
|
|
case ZigTypeIdPromise:
|
|
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);
|
|
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);
|
|
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);
|
|
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:
|
|
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;
|
|
|
|
IrInstruction *prong_value = instruction->prong_value->child;
|
|
if (type_is_invalid(prong_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
assert(target_value_ptr->value.type->id == ZigTypeIdPointer);
|
|
ZigType *target_type = target_value_ptr->value.type->data.pointer.child_type;
|
|
if (target_type->id == ZigTypeIdUnion) {
|
|
ConstExprValue *prong_val = ir_resolve_const(ira, prong_value, UndefBad);
|
|
if (!prong_val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
assert(prong_value->value.type->id == ZigTypeIdEnum);
|
|
TypeUnionField *field = find_union_field_by_tag(target_type, &prong_val->data.x_enum_tag);
|
|
|
|
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, 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, field);
|
|
result->value.type = get_pointer_to_type(ira->codegen, field->type_entry,
|
|
target_value_ptr->value.type->data.pointer.is_const);
|
|
return result;
|
|
} 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_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_container_init_fields_union(IrAnalyze *ira, IrInstruction *instruction,
|
|
ZigType *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields)
|
|
{
|
|
Error err;
|
|
assert(container_type->id == ZigTypeIdUnion);
|
|
|
|
if ((err = type_resolve(ira->codegen, container_type, ResolveStatusSizeKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
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_value = field->value->child;
|
|
if (type_is_invalid(field_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
TypeUnionField *type_field = find_union_type_field(container_type, field->name);
|
|
if (!type_field) {
|
|
ir_add_error_node(ira, field->source_node,
|
|
buf_sprintf("no member named '%s' in union '%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;
|
|
|
|
IrInstruction *casted_field_value = ir_implicit_cast(ira, field_value, type_field->type_entry);
|
|
if (casted_field_value == ira->codegen->invalid_instruction)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if ((err = type_resolve(ira->codegen, casted_field_value->value.type, ResolveStatusZeroBitsKnown)))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope)
|
|
|| type_requires_comptime(ira->codegen, container_type) == ReqCompTimeYes;
|
|
if (is_comptime || casted_field_value->value.special != ConstValSpecialRuntime ||
|
|
!type_has_bits(casted_field_value->value.type))
|
|
{
|
|
ConstExprValue *field_val = ir_resolve_const(ira, casted_field_value, UndefOk);
|
|
if (!field_val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *result = ir_const(ira, instruction, container_type);
|
|
ConstExprValue *out_val = &result->value;
|
|
out_val->data.x_union.payload = field_val;
|
|
out_val->data.x_union.tag = type_field->enum_field->value;
|
|
out_val->parent.id = ConstParentIdUnion;
|
|
out_val->parent.data.p_union.union_val = out_val;
|
|
|
|
return result;
|
|
}
|
|
|
|
IrInstruction *new_instruction = ir_build_union_init(&ira->new_irb,
|
|
instruction->scope, instruction->source_node,
|
|
container_type, type_field, casted_field_value);
|
|
new_instruction->value.type = container_type;
|
|
ir_add_alloca(ira, new_instruction, container_type);
|
|
return new_instruction;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruction *instruction,
|
|
ZigType *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields)
|
|
{
|
|
Error err;
|
|
if (container_type->id == ZigTypeIdUnion) {
|
|
return ir_analyze_container_init_fields_union(ira, instruction, container_type, instr_field_count, fields);
|
|
}
|
|
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);
|
|
|
|
IrInstructionStructInitField *new_fields = allocate<IrInstructionStructInitField>(actual_field_count);
|
|
|
|
bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope)
|
|
|| type_requires_comptime(ira->codegen, container_type) == ReqCompTimeYes;
|
|
|
|
ConstExprValue const_val = {};
|
|
const_val.special = ConstValSpecialStatic;
|
|
const_val.type = container_type;
|
|
const_val.data.x_struct.fields = create_const_vals(actual_field_count);
|
|
for (size_t i = 0; i < instr_field_count; i += 1) {
|
|
IrInstructionContainerInitFieldsField *field = &fields[i];
|
|
|
|
IrInstruction *field_value = field->value->child;
|
|
if (type_is_invalid(field_value->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;
|
|
|
|
IrInstruction *casted_field_value = ir_implicit_cast(ira, field_value, type_field->type_entry);
|
|
if (casted_field_value == ira->codegen->invalid_instruction)
|
|
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;
|
|
|
|
new_fields[field_index].value = casted_field_value;
|
|
new_fields[field_index].type_struct_field = type_field;
|
|
|
|
if (const_val.special == ConstValSpecialStatic) {
|
|
if (is_comptime || casted_field_value->value.special != ConstValSpecialRuntime) {
|
|
ConstExprValue *field_val = ir_resolve_const(ira, casted_field_value, UndefOk);
|
|
if (!field_val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
copy_const_val(&const_val.data.x_struct.fields[field_index], field_val, true);
|
|
} else {
|
|
first_non_const_instruction = casted_field_value;
|
|
const_val.special = ConstValSpecialRuntime;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool any_missing = false;
|
|
for (size_t i = 0; i < actual_field_count; i += 1) {
|
|
if (!field_assign_nodes[i]) {
|
|
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;
|
|
}
|
|
}
|
|
if (any_missing)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (const_val.special == ConstValSpecialStatic) {
|
|
IrInstruction *result = ir_const(ira, instruction, nullptr);
|
|
ConstExprValue *out_val = &result->value;
|
|
copy_const_val(out_val, &const_val, true);
|
|
out_val->type = container_type;
|
|
|
|
for (size_t i = 0; i < instr_field_count; i += 1) {
|
|
ConstExprValue *field_val = &out_val->data.x_struct.fields[i];
|
|
ConstParent *parent = get_const_val_parent(ira->codegen, field_val);
|
|
if (parent != nullptr) {
|
|
parent->id = ConstParentIdStruct;
|
|
parent->data.p_struct.field_index = i;
|
|
parent->data.p_struct.struct_val = out_val;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
IrInstruction *new_instruction = ir_build_struct_init(&ira->new_irb,
|
|
instruction->scope, instruction->source_node,
|
|
container_type, actual_field_count, new_fields);
|
|
new_instruction->value.type = container_type;
|
|
ir_add_alloca(ira, new_instruction, container_type);
|
|
return new_instruction;
|
|
}
|
|
|
|
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 (container_type->id == ZigTypeIdStruct && !is_slice(container_type) && elem_count == 0) {
|
|
return ir_analyze_container_init_fields(ira, &instruction->base, container_type,
|
|
0, nullptr);
|
|
} else if (is_slice(container_type) || container_type->id == ZigTypeIdArray) {
|
|
// array is same as slice init but we make a compile error if the length is wrong
|
|
ZigType *child_type;
|
|
if (container_type->id == ZigTypeIdArray) {
|
|
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;
|
|
}
|
|
} else {
|
|
ZigType *pointer_type = container_type->data.structure.fields[slice_ptr_index].type_entry;
|
|
assert(pointer_type->id == ZigTypeIdPointer);
|
|
child_type = pointer_type->data.pointer.child_type;
|
|
}
|
|
|
|
ZigType *fixed_size_array_type = get_array_type(ira->codegen, child_type, elem_count);
|
|
|
|
ConstExprValue const_val = {};
|
|
const_val.special = ConstValSpecialStatic;
|
|
const_val.type = fixed_size_array_type;
|
|
const_val.data.x_array.data.s_none.elements = create_const_vals(elem_count);
|
|
|
|
bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->base.scope);
|
|
|
|
IrInstruction **new_items = allocate<IrInstruction *>(elem_count);
|
|
|
|
IrInstruction *first_non_const_instruction = nullptr;
|
|
|
|
for (size_t i = 0; i < elem_count; i += 1) {
|
|
IrInstruction *arg_value = instruction->items[i]->child;
|
|
if (type_is_invalid(arg_value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *casted_arg = ir_implicit_cast(ira, arg_value, child_type);
|
|
if (casted_arg == ira->codegen->invalid_instruction)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
new_items[i] = casted_arg;
|
|
|
|
if (const_val.special == ConstValSpecialStatic) {
|
|
if (is_comptime || casted_arg->value.special != ConstValSpecialRuntime) {
|
|
ConstExprValue *elem_val = ir_resolve_const(ira, casted_arg, UndefBad);
|
|
if (!elem_val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
copy_const_val(&const_val.data.x_array.data.s_none.elements[i], elem_val, true);
|
|
} else {
|
|
first_non_const_instruction = casted_arg;
|
|
const_val.special = ConstValSpecialRuntime;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (const_val.special == ConstValSpecialStatic) {
|
|
IrInstruction *result = ir_const(ira, &instruction->base, nullptr);
|
|
ConstExprValue *out_val = &result->value;
|
|
// Make sure to pass same_global_refs=false here in order not to
|
|
// zero the global_refs field for `result` (#1608)
|
|
copy_const_val(out_val, &const_val, false);
|
|
result->value.type = fixed_size_array_type;
|
|
for (size_t i = 0; i < elem_count; i += 1) {
|
|
ConstExprValue *elem_val = &out_val->data.x_array.data.s_none.elements[i];
|
|
ConstParent *parent = get_const_val_parent(ira->codegen, elem_val);
|
|
if (parent != nullptr) {
|
|
parent->id = ConstParentIdArray;
|
|
parent->data.p_array.array_val = out_val;
|
|
parent->data.p_array.elem_index = i;
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
IrInstruction *new_instruction = ir_build_container_init_list(&ira->new_irb,
|
|
instruction->base.scope, instruction->base.source_node,
|
|
nullptr, elem_count, new_items);
|
|
new_instruction->value.type = fixed_size_array_type;
|
|
ir_add_alloca(ira, new_instruction, fixed_size_array_type);
|
|
return new_instruction;
|
|
} else 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);
|
|
} else {
|
|
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;
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
return ir_analyze_container_init_fields(ira, &instruction->base, container_type,
|
|
instruction->field_count, instruction->fields);
|
|
}
|
|
|
|
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);
|
|
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);
|
|
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);
|
|
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);
|
|
|
|
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 = LLVMOffsetOfElement(ira->codegen->target_data_ref, container_type->type_ref, field->gen_index);
|
|
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 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 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);
|
|
assertNoError(ensure_complete_type(ira->codegen, type_info_var->data.x_type));
|
|
|
|
ZigType *type_info_type = type_info_var->data.x_type;
|
|
assert(type_info_type->id == ZigTypeIdUnion);
|
|
|
|
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_defs(IrAnalyze *ira, IrInstruction *source_instr, ConstExprValue *out_val,
|
|
ScopeDecls *decls_scope)
|
|
{
|
|
Error err;
|
|
ZigType *type_info_definition_type = ir_type_info_get_type(ira, "Definition", nullptr);
|
|
if ((err = ensure_complete_type(ira->codegen, type_info_definition_type)))
|
|
return err;
|
|
|
|
ensure_field_index(type_info_definition_type, "name", 0);
|
|
ensure_field_index(type_info_definition_type, "is_pub", 1);
|
|
ensure_field_index(type_info_definition_type, "data", 2);
|
|
|
|
ZigType *type_info_definition_data_type = ir_type_info_get_type(ira, "Data", type_info_definition_type);
|
|
if ((err = ensure_complete_type(ira->codegen, type_info_definition_data_type)))
|
|
return err;
|
|
|
|
ZigType *type_info_fn_def_type = ir_type_info_get_type(ira, "FnDef", type_info_definition_data_type);
|
|
if ((err = ensure_complete_type(ira->codegen, type_info_fn_def_type)))
|
|
return err;
|
|
|
|
ZigType *type_info_fn_def_inline_type = ir_type_info_get_type(ira, "Inline", type_info_fn_def_type);
|
|
if ((err = ensure_complete_type(ira->codegen, type_info_fn_def_inline_type)))
|
|
return err;
|
|
|
|
// Loop through our definitions once to figure out how many definitions we will generate info for.
|
|
auto decl_it = decls_scope->decl_table.entry_iterator();
|
|
decltype(decls_scope->decl_table)::Entry *curr_entry = nullptr;
|
|
int definition_count = 0;
|
|
|
|
while ((curr_entry = decl_it.next()) != nullptr) {
|
|
// If the definition 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;
|
|
}
|
|
|
|
definition_count += 1;
|
|
}
|
|
}
|
|
|
|
ConstExprValue *definition_array = create_const_vals(1);
|
|
definition_array->special = ConstValSpecialStatic;
|
|
definition_array->type = get_array_type(ira->codegen, type_info_definition_type, definition_count);
|
|
definition_array->data.x_array.special = ConstArraySpecialNone;
|
|
definition_array->data.x_array.data.s_none.elements = create_const_vals(definition_count);
|
|
init_const_slice(ira->codegen, out_val, definition_array, 0, definition_count, false);
|
|
|
|
// Loop through the definitions and generate info.
|
|
decl_it = decls_scope->decl_table.entry_iterator();
|
|
curr_entry = nullptr;
|
|
int definition_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 *definition_val = &definition_array->data.x_array.data.s_none.elements[definition_index];
|
|
|
|
definition_val->special = ConstValSpecialStatic;
|
|
definition_val->type = type_info_definition_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_definition_data_type;
|
|
inner_fields[2].parent.id = ConstParentIdStruct;
|
|
inner_fields[2].parent.data.p_struct.struct_val = definition_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 definition.
|
|
// 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.FnDef
|
|
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 = (AstNodeFnProto *)(fn_entry->proto_node);
|
|
|
|
ConstExprValue *fn_def_val = create_const_vals(1);
|
|
fn_def_val->special = ConstValSpecialStatic;
|
|
fn_def_val->type = type_info_fn_def_type;
|
|
fn_def_val->parent.id = ConstParentIdUnion;
|
|
fn_def_val->parent.data.p_union.union_val = &inner_fields[2];
|
|
|
|
ConstExprValue *fn_def_fields = create_const_vals(9);
|
|
fn_def_val->data.x_struct.fields = fn_def_fields;
|
|
|
|
// fn_type: type
|
|
ensure_field_index(fn_def_val->type, "fn_type", 0);
|
|
fn_def_fields[0].special = ConstValSpecialStatic;
|
|
fn_def_fields[0].type = ira->codegen->builtin_types.entry_type;
|
|
fn_def_fields[0].data.x_type = fn_entry->type_entry;
|
|
// inline_type: Data.FnDef.Inline
|
|
ensure_field_index(fn_def_val->type, "inline_type", 1);
|
|
fn_def_fields[1].special = ConstValSpecialStatic;
|
|
fn_def_fields[1].type = type_info_fn_def_inline_type;
|
|
bigint_init_unsigned(&fn_def_fields[1].data.x_enum_tag, fn_entry->fn_inline);
|
|
// calling_convention: TypeInfo.CallingConvention
|
|
ensure_field_index(fn_def_val->type, "calling_convention", 2);
|
|
fn_def_fields[2].special = ConstValSpecialStatic;
|
|
fn_def_fields[2].type = ir_type_info_get_type(ira, "CallingConvention", nullptr);
|
|
bigint_init_unsigned(&fn_def_fields[2].data.x_enum_tag, fn_node->cc);
|
|
// is_var_args: bool
|
|
ensure_field_index(fn_def_val->type, "is_var_args", 3);
|
|
bool is_varargs = fn_node->is_var_args;
|
|
fn_def_fields[3].special = ConstValSpecialStatic;
|
|
fn_def_fields[3].type = ira->codegen->builtin_types.entry_bool;
|
|
fn_def_fields[3].data.x_bool = is_varargs;
|
|
// is_extern: bool
|
|
ensure_field_index(fn_def_val->type, "is_extern", 4);
|
|
fn_def_fields[4].special = ConstValSpecialStatic;
|
|
fn_def_fields[4].type = ira->codegen->builtin_types.entry_bool;
|
|
fn_def_fields[4].data.x_bool = fn_node->is_extern;
|
|
// is_export: bool
|
|
ensure_field_index(fn_def_val->type, "is_export", 5);
|
|
fn_def_fields[5].special = ConstValSpecialStatic;
|
|
fn_def_fields[5].type = ira->codegen->builtin_types.entry_bool;
|
|
fn_def_fields[5].data.x_bool = fn_node->is_export;
|
|
// lib_name: ?[]const u8
|
|
ensure_field_index(fn_def_val->type, "lib_name", 6);
|
|
fn_def_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);
|
|
fn_def_fields[6].type = get_optional_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr));
|
|
if (fn_node->is_extern && buf_len(fn_node->lib_name) > 0) {
|
|
fn_def_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_def_fields[6].data.x_optional, lib_name, 0, buf_len(fn_node->lib_name), true);
|
|
} else {
|
|
fn_def_fields[6].data.x_optional = nullptr;
|
|
}
|
|
// return_type: type
|
|
ensure_field_index(fn_def_val->type, "return_type", 7);
|
|
fn_def_fields[7].special = ConstValSpecialStatic;
|
|
fn_def_fields[7].type = ira->codegen->builtin_types.entry_type;
|
|
fn_def_fields[7].data.x_type = fn_entry->type_entry->data.fn.fn_type_id.return_type;
|
|
// arg_names: [][] const u8
|
|
ensure_field_index(fn_def_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_def_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_def_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();
|
|
}
|
|
|
|
definition_val->data.x_struct.fields = inner_fields;
|
|
definition_index++;
|
|
}
|
|
|
|
assert(definition_index == definition_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(5);
|
|
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;
|
|
|
|
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 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 ZigTypeIdPromise:
|
|
{
|
|
result = create_const_vals(1);
|
|
result->special = ConstValSpecialStatic;
|
|
result->type = ir_type_info_get_type(ira, "Promise", 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);
|
|
|
|
if (type_entry->data.promise.result_type == nullptr)
|
|
fields[0].data.x_optional = nullptr;
|
|
else {
|
|
ConstExprValue *child_type = create_const_vals(1);
|
|
child_type->special = ConstValSpecialStatic;
|
|
child_type->type = ira->codegen->builtin_types.entry_type;
|
|
child_type->data.x_type = type_entry->data.promise.result_type;
|
|
fields[0].data.x_optional = child_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);
|
|
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;
|
|
}
|
|
// defs: []TypeInfo.Definition
|
|
ensure_field_index(result->type, "defs", 3);
|
|
if ((err = ir_make_type_info_defs(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;
|
|
}
|
|
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);
|
|
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;
|
|
}
|
|
// defs: []TypeInfo.Definition
|
|
ensure_field_index(result->type, "defs", 3);
|
|
if ((err = ir_make_type_info_defs(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);
|
|
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 = LLVMOffsetOfElement(ira->codegen->target_data_ref, type_entry->type_ref, struct_field->gen_index);
|
|
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;
|
|
}
|
|
// defs: []TypeInfo.Definition
|
|
ensure_field_index(result->type, "defs", 2);
|
|
if ((err = ir_make_type_info_defs(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(6);
|
|
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;
|
|
}
|
|
// async_allocator_type: type
|
|
ensure_field_index(result->type, "async_allocator_type", 4);
|
|
fields[4].special = ConstValSpecialStatic;
|
|
fields[4].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type);
|
|
if (type_entry->data.fn.fn_type_id.async_allocator_type == nullptr)
|
|
fields[4].data.x_optional = nullptr;
|
|
else {
|
|
ConstExprValue *async_alloc_type = create_const_vals(1);
|
|
async_alloc_type->special = ConstValSpecialStatic;
|
|
async_alloc_type->type = ira->codegen->builtin_types.entry_type;
|
|
async_alloc_type->data.x_type = type_entry->data.fn.fn_type_id.async_allocator_type;
|
|
fields[4].data.x_optional = async_alloc_type;
|
|
}
|
|
// args: []TypeInfo.FnArg
|
|
ZigType *type_info_fn_arg_type = ir_type_info_get_type(ira, "FnArg", nullptr);
|
|
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[5], 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;
|
|
}
|
|
}
|
|
|
|
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));
|
|
}
|
|
|
|
ZigList<ErrorMsg *> errors = {0};
|
|
|
|
Buf *tmp_dep_file = buf_sprintf("%s.d", buf_ptr(&tmp_c_file_path));
|
|
AstNode *root_node;
|
|
if ((err = parse_h_file(&root_node, &errors, buf_ptr(&tmp_c_file_path), ira->codegen, tmp_dep_file))) {
|
|
if (err != ErrorCCompileErrors) {
|
|
ir_add_error_node(ira, node, buf_sprintf("C import failed: %s", err_str(err)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
assert(errors.length > 0);
|
|
|
|
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.length; i += 1) {
|
|
ErrorMsg *err_msg = errors.at(i);
|
|
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(ira->codegen, 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");
|
|
}
|
|
|
|
IrInstruction *result = ir_build_cmpxchg_gen(ira, &instruction->base,
|
|
casted_ptr, casted_cmp_value, casted_new_value,
|
|
success_order, failure_order, instruction->is_weak);
|
|
result->value.type = get_optional_type(ira->codegen, operand_type);
|
|
ir_add_alloca(ira, result, result->value.type);
|
|
return result;
|
|
}
|
|
|
|
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, false);
|
|
} 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);
|
|
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);
|
|
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;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
IrInstruction *result = ir_build_resize_slice(ira, &instruction->base, casted_value, dest_slice_type);
|
|
ir_add_alloca(ira, result, dest_slice_type);
|
|
return result;
|
|
}
|
|
|
|
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);
|
|
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 = ir_build_resize_slice(ira, &instruction->base, target, dest_slice_type);
|
|
ir_add_alloca(ira, result, dest_slice_type);
|
|
return result;
|
|
}
|
|
|
|
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, false);
|
|
}
|
|
|
|
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, false);
|
|
}
|
|
|
|
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, false);
|
|
}
|
|
|
|
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);
|
|
|
|
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) {
|
|
dest_elements[i] = *byte_val;
|
|
}
|
|
|
|
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);
|
|
ZigType *u8_ptr_const = get_pointer_to_type_extra(ira->codegen, u8, true, src_is_volatile,
|
|
PtrLenUnknown, src_align, 0, 0);
|
|
|
|
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) {
|
|
dest_elements[dest_start + i] = src_elements[src_start + i];
|
|
}
|
|
|
|
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, IrInstructionSlice *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);
|
|
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);
|
|
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 *new_instruction = ir_build_slice(&ira->new_irb,
|
|
instruction->base.scope, instruction->base.source_node,
|
|
ptr_ptr, casted_start, end, instruction->safety_check_on);
|
|
new_instruction->value.type = return_type;
|
|
ir_add_alloca(ira, new_instruction, return_type);
|
|
return new_instruction;
|
|
}
|
|
|
|
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_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_handle(IrAnalyze *ira, IrInstructionHandle *instruction) {
|
|
IrInstruction *result = ir_build_handle(&ira->new_irb, instruction->base.scope, instruction->base.source_node);
|
|
ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec);
|
|
assert(fn_entry != nullptr);
|
|
result->value.type = get_promise_type(ira->codegen, fn_entry->type_entry->data.fn.fn_type_id.return_type);
|
|
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 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 ZigTypeIdPromise:
|
|
case ZigTypeIdArray:
|
|
case ZigTypeIdStruct:
|
|
case ZigTypeIdOptional:
|
|
case ZigTypeIdErrorUnion:
|
|
case ZigTypeIdErrorSet:
|
|
case ZigTypeIdEnum:
|
|
case ZigTypeIdUnion:
|
|
case ZigTypeIdFn:
|
|
case ZigTypeIdVector:
|
|
{
|
|
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);
|
|
} 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 IrInstruction *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstructionTestErr *instruction) {
|
|
IrInstruction *value = instruction->value->child;
|
|
if (type_is_invalid(value->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigType *type_entry = value->value.type;
|
|
if (type_is_invalid(type_entry)) {
|
|
return ira->codegen->invalid_instruction;
|
|
} else 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));
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
IrInstruction *result = ir_build_test_err(&ira->new_irb,
|
|
instruction->base.scope, instruction->base.source_node, value);
|
|
result->value.type = ira->codegen->builtin_types.entry_bool;
|
|
return result;
|
|
} 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_instruction_unwrap_err_code(IrAnalyze *ira, IrInstructionUnwrapErrCode *instruction) {
|
|
IrInstruction *base_ptr = instruction->err_union->child;
|
|
if (type_is_invalid(base_ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
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;
|
|
}
|
|
|
|
if (instr_is_comptime(base_ptr)) {
|
|
ConstExprValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad);
|
|
if (!ptr_val)
|
|
return ira->codegen->invalid_instruction;
|
|
ConstExprValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, instruction->base.source_node);
|
|
if (err_union_val == nullptr)
|
|
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;
|
|
assert(err);
|
|
|
|
IrInstruction *result = ir_const(ira, &instruction->base,
|
|
type_entry->data.error_union.err_set_type);
|
|
result->value.data.x_err_set = err;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
IrInstruction *result = ir_build_unwrap_err_code(&ira->new_irb,
|
|
instruction->base.scope, instruction->base.source_node, base_ptr);
|
|
result->value.type = type_entry->data.error_union.err_set_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;
|
|
ZigType *ptr_type = value->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, value,
|
|
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);
|
|
if (instr_is_comptime(value)) {
|
|
ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad);
|
|
if (!ptr_val)
|
|
return ira->codegen->invalid_instruction;
|
|
ConstExprValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, instruction->base.source_node);
|
|
if (err_union_val == nullptr)
|
|
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;
|
|
if (err != nullptr) {
|
|
ir_add_error(ira, &instruction->base,
|
|
buf_sprintf("caught unexpected error '%s'", buf_ptr(&err->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *result = ir_const(ira, &instruction->base, 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;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
IrInstruction *result = ir_build_unwrap_err_payload(&ira->new_irb,
|
|
instruction->base.scope, instruction->base.source_node, value, instruction->safety_check_on);
|
|
result->value.type = result_type;
|
|
return result;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
if (fn_type_id.cc == CallingConventionAsync) {
|
|
if (instruction->async_allocator_type_value == nullptr) {
|
|
ir_add_error(ira, &instruction->base,
|
|
buf_sprintf("async fn proto missing allocator type"));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
IrInstruction *async_allocator_type_value = instruction->async_allocator_type_value->child;
|
|
fn_type_id.async_allocator_type = ir_resolve_type(ira, async_allocator_type_value);
|
|
if (type_is_invalid(fn_type_id.async_allocator_type))
|
|
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 = range->start->child;
|
|
if (type_is_invalid(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;
|
|
|
|
if (start_value->value.type->id != ZigTypeIdEnum) {
|
|
ir_add_error(ira, range->start, buf_sprintf("not an enum 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 = range->start->child;
|
|
if (type_is_invalid(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;
|
|
|
|
assert(start_value->value.type->id == ZigTypeIdErrorSet);
|
|
uint32_t start_index = start_value->value.data.x_err_set->value;
|
|
|
|
assert(end_value->value.type->id == ZigTypeIdErrorSet);
|
|
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) {
|
|
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);
|
|
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, false);
|
|
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 = ir_const(ira, source_instr, dest_type);
|
|
copy_const_val(&result->value, val, false);
|
|
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 ZigTypeIdUndefined:
|
|
case ZigTypeIdNull:
|
|
case ZigTypeIdPromise:
|
|
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 *type_field = &val->type->data.structure.fields[field_i];
|
|
if (type_field->gen_index == SIZE_MAX)
|
|
continue;
|
|
ConstExprValue *field_val = &val->data.x_struct.fields[field_i];
|
|
size_t offset = LLVMOffsetOfElement(codegen->target_data_ref, val->type->type_ref,
|
|
type_field->gen_index);
|
|
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) {
|
|
LLVMTypeRef gen_llvm_int_type = LLVMStructGetTypeAtIndex(val->type->type_ref,
|
|
(unsigned)gen_i);
|
|
size_t big_int_bit_count = LLVMGetIntTypeWidth(gen_llvm_int_type);
|
|
size_t big_int_byte_count = big_int_bit_count / 8;
|
|
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_bit_count, is_big_endian);
|
|
offset += big_int_byte_count;
|
|
gen_i += 1;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
case ZigTypeIdOptional:
|
|
zig_panic("TODO buf_write_value_bytes maybe type");
|
|
case ZigTypeIdErrorUnion:
|
|
zig_panic("TODO buf_write_value_bytes error union");
|
|
case ZigTypeIdErrorSet:
|
|
zig_panic("TODO buf_write_value_bytes pure error type");
|
|
case ZigTypeIdFn:
|
|
zig_panic("TODO buf_write_value_bytes fn type");
|
|
case ZigTypeIdUnion:
|
|
zig_panic("TODO buf_write_value_bytes union 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;
|
|
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 ZigTypeIdUndefined:
|
|
case ZigTypeIdNull:
|
|
case ZigTypeIdPromise:
|
|
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;
|
|
assert(tag_int_type->id == ZigTypeIdInt);
|
|
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 *type_field = &val->type->data.structure.fields[field_i];
|
|
field_val->type = type_field->type_entry;
|
|
if (type_field->gen_index == SIZE_MAX)
|
|
continue;
|
|
size_t offset = LLVMOffsetOfElement(codegen->target_data_ref, val->type->type_ref,
|
|
type_field->gen_index);
|
|
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) {
|
|
LLVMTypeRef gen_llvm_int_type = LLVMStructGetTypeAtIndex(val->type->type_ref,
|
|
(unsigned)gen_i);
|
|
size_t big_int_bit_count = LLVMGetIntTypeWidth(gen_llvm_int_type);
|
|
size_t big_int_byte_count = big_int_bit_count / 8;
|
|
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_bit_count, is_big_endian, false);
|
|
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;
|
|
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_bit_count, 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");
|
|
}
|
|
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 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 IrInstruction *ir_analyze_bit_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value,
|
|
ZigType *dest_type)
|
|
{
|
|
Error err;
|
|
|
|
ZigType *src_type = value->value.type;
|
|
assert(get_codegen_ptr_type(src_type) == nullptr);
|
|
assert(type_can_bit_cast(src_type));
|
|
assert(get_codegen_ptr_type(dest_type) == nullptr);
|
|
assert(type_can_bit_cast(dest_type));
|
|
|
|
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;
|
|
}
|
|
|
|
IrInstruction *result = ir_build_bit_cast_gen(ira, source_instr, value, dest_type);
|
|
if (handle_is_ptr(dest_type) && !handle_is_ptr(src_type)) {
|
|
ir_add_alloca(ira, result, dest_type);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstructionBitCast *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 *value = instruction->value->child;
|
|
ZigType *src_type = value->value.type;
|
|
if (type_is_invalid(src_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (get_codegen_ptr_type(src_type) != nullptr) {
|
|
ir_add_error(ira, value,
|
|
buf_sprintf("unable to @bitCast from pointer type '%s'", buf_ptr(&src_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (!type_can_bit_cast(src_type)) {
|
|
ir_add_error(ira, dest_type_value,
|
|
buf_sprintf("unable to @bitCast from type '%s'", buf_ptr(&src_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (get_codegen_ptr_type(dest_type) != nullptr) {
|
|
ir_add_error(ira, dest_type_value,
|
|
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, dest_type_value,
|
|
buf_sprintf("unable to @bitCast to type '%s'", buf_ptr(&dest_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
return ir_analyze_bit_cast(ira, &instruction->base, value, dest_type);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_int_to_ptr(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target,
|
|
ZigType *ptr_type)
|
|
{
|
|
assert(get_src_ptr_type(ptr_type) != nullptr);
|
|
assert(type_has_bits(ptr_type));
|
|
|
|
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;
|
|
|
|
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 = bigint_as_unsigned(&val->data.x_bigint);
|
|
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)
|
|
{
|
|
Tld *tld = instruction->tld;
|
|
LVal lval = instruction->lval;
|
|
|
|
resolve_top_level_decl(ira->codegen, tld, instruction->base.source_node);
|
|
if (tld->resolution == TldResolutionInvalid)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
switch (tld->id) {
|
|
case TldIdContainer:
|
|
case TldIdCompTime:
|
|
zig_unreachable();
|
|
case TldIdVar: {
|
|
TldVar *tld_var = (TldVar *)tld;
|
|
ZigVar *var = tld_var->var;
|
|
|
|
if (var == nullptr) {
|
|
return ir_error_dependency_loop(ira, &instruction->base);
|
|
}
|
|
|
|
IrInstruction *var_ptr = ir_get_var_ptr(ira, &instruction->base, var);
|
|
if (type_is_invalid(var_ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (tld_var->extern_lib_name != nullptr) {
|
|
add_link_lib_symbol(ira, tld_var->extern_lib_name, &var->name, instruction->base.source_node);
|
|
}
|
|
|
|
if (lval == LValPtr) {
|
|
return var_ptr;
|
|
} else {
|
|
return ir_get_deref(ira, &instruction->base, var_ptr);
|
|
}
|
|
}
|
|
case TldIdFn: {
|
|
TldFn *tld_fn = (TldFn *)tld;
|
|
ZigFn *fn_entry = tld_fn->fn_entry;
|
|
assert(fn_entry->type_entry);
|
|
|
|
if (tld_fn->extern_lib_name != nullptr) {
|
|
add_link_lib_symbol(ira, tld_fn->extern_lib_name, &fn_entry->symbol_name, instruction->base.source_node);
|
|
}
|
|
|
|
IrInstruction *ref_instruction = ir_create_const_fn(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node, fn_entry);
|
|
if (lval == LValPtr) {
|
|
return ir_get_ref(ira, &instruction->base, ref_instruction, true, false);
|
|
} else {
|
|
return ref_instruction;
|
|
}
|
|
}
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
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.
|
|
assert(fn_type->data.fn.is_generic);
|
|
|
|
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) {
|
|
IrInstruction *target_inst = instruction->target->child;
|
|
if (type_is_invalid(target_inst->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
IrInstruction *casted_target = ir_implicit_cast(ira, target_inst, ira->codegen->builtin_types.entry_promise);
|
|
if (type_is_invalid(casted_target->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *result = ir_build_cancel(&ira->new_irb, instruction->base.scope, instruction->base.source_node, casted_target);
|
|
result->value.type = ira->codegen->builtin_types.entry_void;
|
|
result->value.special = ConstValSpecialStatic;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_coro_id(IrAnalyze *ira, IrInstructionCoroId *instruction) {
|
|
IrInstruction *promise_ptr = instruction->promise_ptr->child;
|
|
if (type_is_invalid(promise_ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *result = ir_build_coro_id(&ira->new_irb, instruction->base.scope, instruction->base.source_node,
|
|
promise_ptr);
|
|
result->value.type = ira->codegen->builtin_types.entry_usize;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_coro_alloc(IrAnalyze *ira, IrInstructionCoroAlloc *instruction) {
|
|
IrInstruction *coro_id = instruction->coro_id->child;
|
|
if (type_is_invalid(coro_id->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *result = ir_build_coro_alloc(&ira->new_irb, instruction->base.scope, instruction->base.source_node,
|
|
coro_id);
|
|
result->value.type = ira->codegen->builtin_types.entry_bool;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_coro_size(IrAnalyze *ira, IrInstructionCoroSize *instruction) {
|
|
IrInstruction *result = ir_build_coro_size(&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_coro_begin(IrAnalyze *ira, IrInstructionCoroBegin *instruction) {
|
|
IrInstruction *coro_id = instruction->coro_id->child;
|
|
if (type_is_invalid(coro_id->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *coro_mem_ptr = instruction->coro_mem_ptr->child;
|
|
if (type_is_invalid(coro_mem_ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec);
|
|
assert(fn_entry != nullptr);
|
|
IrInstruction *result = ir_build_coro_begin(&ira->new_irb, instruction->base.scope, instruction->base.source_node,
|
|
coro_id, coro_mem_ptr);
|
|
result->value.type = get_promise_type(ira->codegen, fn_entry->type_entry->data.fn.fn_type_id.return_type);
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_get_implicit_allocator(IrAnalyze *ira, IrInstructionGetImplicitAllocator *instruction) {
|
|
return ir_get_implicit_allocator(ira, &instruction->base, instruction->id);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_coro_alloc_fail(IrAnalyze *ira, IrInstructionCoroAllocFail *instruction) {
|
|
IrInstruction *err_val = instruction->err_val->child;
|
|
if (type_is_invalid(err_val->value.type))
|
|
return ir_unreach_error(ira);
|
|
|
|
IrInstruction *result = ir_build_coro_alloc_fail(&ira->new_irb, instruction->base.scope, instruction->base.source_node, err_val);
|
|
result->value.type = ira->codegen->builtin_types.entry_unreachable;
|
|
return ir_finish_anal(ira, result);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_coro_suspend(IrAnalyze *ira, IrInstructionCoroSuspend *instruction) {
|
|
IrInstruction *save_point = nullptr;
|
|
if (instruction->save_point != nullptr) {
|
|
save_point = instruction->save_point->child;
|
|
if (type_is_invalid(save_point->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *is_final = instruction->is_final->child;
|
|
if (type_is_invalid(is_final->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *result = ir_build_coro_suspend(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node, save_point, is_final);
|
|
result->value.type = ira->codegen->builtin_types.entry_u8;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_coro_end(IrAnalyze *ira, IrInstructionCoroEnd *instruction) {
|
|
IrInstruction *result = ir_build_coro_end(&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_coro_free(IrAnalyze *ira, IrInstructionCoroFree *instruction) {
|
|
IrInstruction *coro_id = instruction->coro_id->child;
|
|
if (type_is_invalid(coro_id->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *coro_handle = instruction->coro_handle->child;
|
|
if (type_is_invalid(coro_handle->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *result = ir_build_coro_free(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node, coro_id, coro_handle);
|
|
ZigType *ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, false);
|
|
result->value.type = get_optional_type(ira->codegen, ptr_type);
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_coro_resume(IrAnalyze *ira, IrInstructionCoroResume *instruction) {
|
|
IrInstruction *awaiter_handle = instruction->awaiter_handle->child;
|
|
if (type_is_invalid(awaiter_handle->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *casted_target = ir_implicit_cast(ira, awaiter_handle, ira->codegen->builtin_types.entry_promise);
|
|
if (type_is_invalid(casted_target->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *result = ir_build_coro_resume(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node, casted_target);
|
|
result->value.type = ira->codegen->builtin_types.entry_void;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_coro_save(IrAnalyze *ira, IrInstructionCoroSave *instruction) {
|
|
IrInstruction *coro_handle = instruction->coro_handle->child;
|
|
if (type_is_invalid(coro_handle->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *result = ir_build_coro_save(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node, coro_handle);
|
|
result->value.type = ira->codegen->builtin_types.entry_usize;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_coro_promise(IrAnalyze *ira, IrInstructionCoroPromise *instruction) {
|
|
IrInstruction *coro_handle = instruction->coro_handle->child;
|
|
if (type_is_invalid(coro_handle->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (coro_handle->value.type->id != ZigTypeIdPromise ||
|
|
coro_handle->value.type->data.promise.result_type == nullptr)
|
|
{
|
|
ir_add_error(ira, &instruction->base, buf_sprintf("expected promise->T, found '%s'",
|
|
buf_ptr(&coro_handle->value.type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
ZigType *coro_frame_type = get_promise_frame_type(ira->codegen,
|
|
coro_handle->value.type->data.promise.result_type);
|
|
|
|
IrInstruction *result = ir_build_coro_promise(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node, coro_handle);
|
|
result->value.type = get_pointer_to_type(ira->codegen, coro_frame_type, false);
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_coro_alloc_helper(IrAnalyze *ira, IrInstructionCoroAllocHelper *instruction) {
|
|
IrInstruction *realloc_fn = instruction->realloc_fn->child;
|
|
if (type_is_invalid(realloc_fn->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *coro_size = instruction->coro_size->child;
|
|
if (type_is_invalid(coro_size->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *result = ir_build_coro_alloc_helper(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node, realloc_fn, coro_size);
|
|
ZigType *u8_ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, false);
|
|
result->value.type = get_optional_type(ira->codegen, u8_ptr_type);
|
|
return result;
|
|
}
|
|
|
|
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;
|
|
}
|
|
if (operand_type->data.integral.bit_count > ira->codegen->pointer_size_bytes * 8) {
|
|
ir_add_error(ira, op,
|
|
buf_sprintf("expected integer type pointer size or smaller, found %" PRIu32 "-bit integer type",
|
|
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) {
|
|
assert(instruction->ordering != nullptr);
|
|
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);
|
|
assert(result->value.type != nullptr);
|
|
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_promise_result_type(IrAnalyze *ira, IrInstructionPromiseResultType *instruction) {
|
|
ZigType *promise_type = ir_resolve_type(ira, instruction->promise_type->child);
|
|
if (type_is_invalid(promise_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (promise_type->id != ZigTypeIdPromise || promise_type->data.promise.result_type == nullptr) {
|
|
ir_add_error(ira, &instruction->base, buf_sprintf("expected promise->T, found '%s'",
|
|
buf_ptr(&promise_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
return ir_const_type(ira, &instruction->base, promise_type->data.promise.result_type);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_await_bookkeeping(IrAnalyze *ira, IrInstructionAwaitBookkeeping *instruction) {
|
|
ZigType *promise_result_type = ir_resolve_type(ira, instruction->promise_result_type->child);
|
|
if (type_is_invalid(promise_result_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec);
|
|
assert(fn_entry != nullptr);
|
|
|
|
if (type_can_fail(promise_result_type)) {
|
|
fn_entry->calls_or_awaits_errorable_fn = true;
|
|
}
|
|
|
|
return ir_const_void(ira, &instruction->base);
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_merge_err_ret_traces(IrAnalyze *ira,
|
|
IrInstructionMergeErrRetTraces *instruction)
|
|
{
|
|
IrInstruction *coro_promise_ptr = instruction->coro_promise_ptr->child;
|
|
if (type_is_invalid(coro_promise_ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
assert(coro_promise_ptr->value.type->id == ZigTypeIdPointer);
|
|
ZigType *promise_frame_type = coro_promise_ptr->value.type->data.pointer.child_type;
|
|
assert(promise_frame_type->id == ZigTypeIdStruct);
|
|
ZigType *promise_result_type = promise_frame_type->data.structure.fields[1].type_entry;
|
|
|
|
if (!type_can_fail(promise_result_type)) {
|
|
return ir_const_void(ira, &instruction->base);
|
|
}
|
|
|
|
IrInstruction *src_err_ret_trace_ptr = instruction->src_err_ret_trace_ptr->child;
|
|
if (type_is_invalid(src_err_ret_trace_ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *dest_err_ret_trace_ptr = instruction->dest_err_ret_trace_ptr->child;
|
|
if (type_is_invalid(dest_err_ret_trace_ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *result = ir_build_merge_err_ret_traces(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node, coro_promise_ptr, src_err_ret_trace_ptr, dest_err_ret_trace_ptr);
|
|
result->value.type = ira->codegen->builtin_types.entry_void;
|
|
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 IrInstruction *ir_analyze_instruction_mark_err_ret_trace_ptr(IrAnalyze *ira, IrInstructionMarkErrRetTracePtr *instruction) {
|
|
IrInstruction *err_ret_trace_ptr = instruction->err_ret_trace_ptr->child;
|
|
if (type_is_invalid(err_ret_trace_ptr->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *result = ir_build_mark_err_ret_trace_ptr(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node, err_ret_trace_ptr);
|
|
result->value.type = ira->codegen->builtin_types.entry_void;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstructionSqrt *instruction) {
|
|
ZigType *float_type = ir_resolve_type(ira, instruction->type->child);
|
|
if (type_is_invalid(float_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *op = instruction->op->child;
|
|
if (type_is_invalid(op->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
bool ok_type = float_type->id == ZigTypeIdComptimeFloat || float_type->id == ZigTypeIdFloat;
|
|
if (!ok_type) {
|
|
ir_add_error(ira, instruction->type, buf_sprintf("@sqrt does not support type '%s'", buf_ptr(&float_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *casted_op = ir_implicit_cast(ira, op, float_type);
|
|
if (type_is_invalid(casted_op->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (instr_is_comptime(casted_op)) {
|
|
ConstExprValue *val = ir_resolve_const(ira, casted_op, UndefBad);
|
|
if (!val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *result = ir_const(ira, &instruction->base, float_type);
|
|
ConstExprValue *out_val = &result->value;
|
|
|
|
if (float_type->id == ZigTypeIdComptimeFloat) {
|
|
bigfloat_sqrt(&out_val->data.x_bigfloat, &val->data.x_bigfloat);
|
|
} else if (float_type->id == ZigTypeIdFloat) {
|
|
switch (float_type->data.floating.bit_count) {
|
|
case 16:
|
|
out_val->data.x_f16 = f16_sqrt(val->data.x_f16);
|
|
break;
|
|
case 32:
|
|
out_val->data.x_f32 = sqrtf(val->data.x_f32);
|
|
break;
|
|
case 64:
|
|
out_val->data.x_f64 = sqrt(val->data.x_f64);
|
|
break;
|
|
case 128:
|
|
f128M_sqrt(&val->data.x_f128, &out_val->data.x_f128);
|
|
break;
|
|
default:
|
|
zig_unreachable();
|
|
}
|
|
} else {
|
|
zig_unreachable();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
assert(float_type->id == ZigTypeIdFloat);
|
|
if (float_type->data.floating.bit_count != 16 &&
|
|
float_type->data.floating.bit_count != 32 &&
|
|
float_type->data.floating.bit_count != 64) {
|
|
ir_add_error(ira, instruction->type, buf_sprintf("compiler TODO: add implementation of sqrt for '%s'", buf_ptr(&float_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *result = ir_build_sqrt(&ira->new_irb, instruction->base.scope,
|
|
instruction->base.source_node, nullptr, casted_op);
|
|
result->value.type = float_type;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_bswap(IrAnalyze *ira, IrInstructionBswap *instruction) {
|
|
ZigType *int_type = ir_resolve_type(ira, instruction->type->child);
|
|
if (type_is_invalid(int_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *op = instruction->op->child;
|
|
if (type_is_invalid(op->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (int_type->id != ZigTypeIdInt) {
|
|
ir_add_error(ira, instruction->type,
|
|
buf_sprintf("expected integer type, found '%s'", buf_ptr(&int_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
if (int_type->data.integral.bit_count % 8 != 0) {
|
|
ir_add_error(ira, instruction->type,
|
|
buf_sprintf("@bswap 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;
|
|
}
|
|
|
|
IrInstruction *casted_op = ir_implicit_cast(ira, op, int_type);
|
|
if (type_is_invalid(casted_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 casted_op;
|
|
}
|
|
|
|
if (instr_is_comptime(casted_op)) {
|
|
ConstExprValue *val = ir_resolve_const(ira, casted_op, UndefBad);
|
|
if (!val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
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, casted_op);
|
|
result->value.type = int_type;
|
|
return result;
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction_bit_reverse(IrAnalyze *ira, IrInstructionBitReverse *instruction) {
|
|
ZigType *int_type = ir_resolve_type(ira, instruction->type->child);
|
|
if (type_is_invalid(int_type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
IrInstruction *op = instruction->op->child;
|
|
if (type_is_invalid(op->value.type))
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
if (int_type->id != ZigTypeIdInt) {
|
|
ir_add_error(ira, instruction->type,
|
|
buf_sprintf("expected integer type, found '%s'", buf_ptr(&int_type->name)));
|
|
return ira->codegen->invalid_instruction;
|
|
}
|
|
|
|
IrInstruction *casted_op = ir_implicit_cast(ira, op, int_type);
|
|
if (type_is_invalid(casted_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(casted_op)) {
|
|
ConstExprValue *val = ir_resolve_const(ira, casted_op, UndefBad);
|
|
if (!val)
|
|
return ira->codegen->invalid_instruction;
|
|
|
|
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, casted_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_nocast(IrAnalyze *ira, IrInstruction *instruction) {
|
|
switch (instruction->id) {
|
|
case IrInstructionIdInvalid:
|
|
case IrInstructionIdWidenOrShorten:
|
|
case IrInstructionIdStructInit:
|
|
case IrInstructionIdUnionInit:
|
|
case IrInstructionIdStructFieldPtr:
|
|
case IrInstructionIdUnionFieldPtr:
|
|
case IrInstructionIdOptionalWrap:
|
|
case IrInstructionIdErrWrapCode:
|
|
case IrInstructionIdErrWrapPayload:
|
|
case IrInstructionIdCast:
|
|
case IrInstructionIdDeclVarGen:
|
|
case IrInstructionIdPtrCastGen:
|
|
case IrInstructionIdCmpxchgGen:
|
|
case IrInstructionIdArrayToVector:
|
|
case IrInstructionIdVectorToArray:
|
|
case IrInstructionIdAssertZero:
|
|
case IrInstructionIdResizeSlice:
|
|
case IrInstructionIdLoadPtrGen:
|
|
case IrInstructionIdBitCastGen:
|
|
zig_unreachable();
|
|
|
|
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 IrInstructionIdCall:
|
|
return ir_analyze_instruction_call(ira, (IrInstructionCall *)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 IrInstructionIdToPtrType:
|
|
return ir_analyze_instruction_to_ptr_type(ira, (IrInstructionToPtrType *)instruction);
|
|
case IrInstructionIdPtrTypeChild:
|
|
return ir_analyze_instruction_ptr_type_child(ira, (IrInstructionPtrTypeChild *)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 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 IrInstructionIdPromiseType:
|
|
return ir_analyze_instruction_promise_type(ira, (IrInstructionPromiseType *)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 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 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 IrInstructionIdSlice:
|
|
return ir_analyze_instruction_slice(ira, (IrInstructionSlice *)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 IrInstructionIdHandle:
|
|
return ir_analyze_instruction_handle(ira, (IrInstructionHandle *)instruction);
|
|
case IrInstructionIdAlignOf:
|
|
return ir_analyze_instruction_align_of(ira, (IrInstructionAlignOf *)instruction);
|
|
case IrInstructionIdOverflowOp:
|
|
return ir_analyze_instruction_overflow_op(ira, (IrInstructionOverflowOp *)instruction);
|
|
case IrInstructionIdTestErr:
|
|
return ir_analyze_instruction_test_err(ira, (IrInstructionTestErr *)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 IrInstructionIdBitCast:
|
|
return ir_analyze_instruction_bit_cast(ira, (IrInstructionBitCast *)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 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 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 IrInstructionIdCoroId:
|
|
return ir_analyze_instruction_coro_id(ira, (IrInstructionCoroId *)instruction);
|
|
case IrInstructionIdCoroAlloc:
|
|
return ir_analyze_instruction_coro_alloc(ira, (IrInstructionCoroAlloc *)instruction);
|
|
case IrInstructionIdCoroSize:
|
|
return ir_analyze_instruction_coro_size(ira, (IrInstructionCoroSize *)instruction);
|
|
case IrInstructionIdCoroBegin:
|
|
return ir_analyze_instruction_coro_begin(ira, (IrInstructionCoroBegin *)instruction);
|
|
case IrInstructionIdGetImplicitAllocator:
|
|
return ir_analyze_instruction_get_implicit_allocator(ira, (IrInstructionGetImplicitAllocator *)instruction);
|
|
case IrInstructionIdCoroAllocFail:
|
|
return ir_analyze_instruction_coro_alloc_fail(ira, (IrInstructionCoroAllocFail *)instruction);
|
|
case IrInstructionIdCoroSuspend:
|
|
return ir_analyze_instruction_coro_suspend(ira, (IrInstructionCoroSuspend *)instruction);
|
|
case IrInstructionIdCoroEnd:
|
|
return ir_analyze_instruction_coro_end(ira, (IrInstructionCoroEnd *)instruction);
|
|
case IrInstructionIdCoroFree:
|
|
return ir_analyze_instruction_coro_free(ira, (IrInstructionCoroFree *)instruction);
|
|
case IrInstructionIdCoroResume:
|
|
return ir_analyze_instruction_coro_resume(ira, (IrInstructionCoroResume *)instruction);
|
|
case IrInstructionIdCoroSave:
|
|
return ir_analyze_instruction_coro_save(ira, (IrInstructionCoroSave *)instruction);
|
|
case IrInstructionIdCoroPromise:
|
|
return ir_analyze_instruction_coro_promise(ira, (IrInstructionCoroPromise *)instruction);
|
|
case IrInstructionIdCoroAllocHelper:
|
|
return ir_analyze_instruction_coro_alloc_helper(ira, (IrInstructionCoroAllocHelper *)instruction);
|
|
case IrInstructionIdAtomicRmw:
|
|
return ir_analyze_instruction_atomic_rmw(ira, (IrInstructionAtomicRmw *)instruction);
|
|
case IrInstructionIdAtomicLoad:
|
|
return ir_analyze_instruction_atomic_load(ira, (IrInstructionAtomicLoad *)instruction);
|
|
case IrInstructionIdPromiseResultType:
|
|
return ir_analyze_instruction_promise_result_type(ira, (IrInstructionPromiseResultType *)instruction);
|
|
case IrInstructionIdAwaitBookkeeping:
|
|
return ir_analyze_instruction_await_bookkeeping(ira, (IrInstructionAwaitBookkeeping *)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 IrInstructionIdMergeErrRetTraces:
|
|
return ir_analyze_instruction_merge_err_ret_traces(ira, (IrInstructionMergeErrRetTraces *)instruction);
|
|
case IrInstructionIdMarkErrRetTracePtr:
|
|
return ir_analyze_instruction_mark_err_ret_trace_ptr(ira, (IrInstructionMarkErrRetTracePtr *)instruction);
|
|
case IrInstructionIdSqrt:
|
|
return ir_analyze_instruction_sqrt(ira, (IrInstructionSqrt *)instruction);
|
|
case IrInstructionIdBswap:
|
|
return ir_analyze_instruction_bswap(ira, (IrInstructionBswap *)instruction);
|
|
case IrInstructionIdBitReverse:
|
|
return ir_analyze_instruction_bit_reverse(ira, (IrInstructionBitReverse *)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);
|
|
}
|
|
zig_unreachable();
|
|
}
|
|
|
|
static IrInstruction *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *old_instruction) {
|
|
IrInstruction *new_instruction = ir_analyze_instruction_nocast(ira, old_instruction);
|
|
assert(new_instruction->value.type != nullptr);
|
|
old_instruction->child = new_instruction;
|
|
return new_instruction;
|
|
}
|
|
|
|
// 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;
|
|
|
|
ZigFn *fn_entry = exec_fn_entry(old_exec);
|
|
bool is_async = fn_entry != nullptr && fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync;
|
|
ira->explicit_return_type = is_async ? get_promise_type(codegen, expected_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;
|
|
}
|
|
|
|
IrInstruction *new_instruction = ir_analyze_instruction(ira, old_instruction);
|
|
if (type_is_invalid(new_instruction->value.type) && ir_should_inline(new_exec, old_instruction->scope)) {
|
|
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 IrInstructionIdCall:
|
|
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 IrInstructionIdCoroId:
|
|
case IrInstructionIdCoroBegin:
|
|
case IrInstructionIdCoroAllocFail:
|
|
case IrInstructionIdCoroEnd:
|
|
case IrInstructionIdCoroResume:
|
|
case IrInstructionIdCoroSave:
|
|
case IrInstructionIdCoroAllocHelper:
|
|
case IrInstructionIdAwaitBookkeeping:
|
|
case IrInstructionIdSaveErrRetAddr:
|
|
case IrInstructionIdAddImplicitReturnType:
|
|
case IrInstructionIdMergeErrRetTraces:
|
|
case IrInstructionIdMarkErrRetTracePtr:
|
|
case IrInstructionIdAtomicRmw:
|
|
case IrInstructionIdCmpxchgGen:
|
|
case IrInstructionIdCmpxchgSrc:
|
|
case IrInstructionIdAssertZero:
|
|
case IrInstructionIdResizeSlice:
|
|
case IrInstructionIdGlobalAsm:
|
|
return true;
|
|
|
|
case IrInstructionIdPhi:
|
|
case IrInstructionIdUnOp:
|
|
case IrInstructionIdBinOp:
|
|
case IrInstructionIdLoadPtr:
|
|
case IrInstructionIdLoadPtrGen:
|
|
case IrInstructionIdConst:
|
|
case IrInstructionIdCast:
|
|
case IrInstructionIdContainerInitList:
|
|
case IrInstructionIdContainerInitFields:
|
|
case IrInstructionIdStructInit:
|
|
case IrInstructionIdUnionInit:
|
|
case IrInstructionIdFieldPtr:
|
|
case IrInstructionIdElemPtr:
|
|
case IrInstructionIdVarPtr:
|
|
case IrInstructionIdTypeOf:
|
|
case IrInstructionIdToPtrType:
|
|
case IrInstructionIdPtrTypeChild:
|
|
case IrInstructionIdStructFieldPtr:
|
|
case IrInstructionIdUnionFieldPtr:
|
|
case IrInstructionIdArrayType:
|
|
case IrInstructionIdPromiseType:
|
|
case IrInstructionIdSliceType:
|
|
case IrInstructionIdSizeOf:
|
|
case IrInstructionIdTestNonNull:
|
|
case IrInstructionIdOptionalUnwrapPtr:
|
|
case IrInstructionIdClz:
|
|
case IrInstructionIdCtz:
|
|
case IrInstructionIdPopCount:
|
|
case IrInstructionIdSwitchVar:
|
|
case IrInstructionIdSwitchTarget:
|
|
case IrInstructionIdUnionTag:
|
|
case IrInstructionIdRef:
|
|
case IrInstructionIdEmbedFile:
|
|
case IrInstructionIdTruncate:
|
|
case IrInstructionIdIntType:
|
|
case IrInstructionIdVectorType:
|
|
case IrInstructionIdBoolNot:
|
|
case IrInstructionIdSlice:
|
|
case IrInstructionIdMemberCount:
|
|
case IrInstructionIdMemberType:
|
|
case IrInstructionIdMemberName:
|
|
case IrInstructionIdAlignOf:
|
|
case IrInstructionIdReturnAddress:
|
|
case IrInstructionIdFrameAddress:
|
|
case IrInstructionIdHandle:
|
|
case IrInstructionIdTestErr:
|
|
case IrInstructionIdUnwrapErrCode:
|
|
case IrInstructionIdOptionalWrap:
|
|
case IrInstructionIdErrWrapCode:
|
|
case IrInstructionIdErrWrapPayload:
|
|
case IrInstructionIdFnProto:
|
|
case IrInstructionIdTestComptime:
|
|
case IrInstructionIdPtrCastSrc:
|
|
case IrInstructionIdPtrCastGen:
|
|
case IrInstructionIdBitCast:
|
|
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 IrInstructionIdTypeId:
|
|
case IrInstructionIdAlignCast:
|
|
case IrInstructionIdOpaqueType:
|
|
case IrInstructionIdArgType:
|
|
case IrInstructionIdTagType:
|
|
case IrInstructionIdErrorReturnTrace:
|
|
case IrInstructionIdErrorUnion:
|
|
case IrInstructionIdGetImplicitAllocator:
|
|
case IrInstructionIdCoroAlloc:
|
|
case IrInstructionIdCoroSize:
|
|
case IrInstructionIdCoroSuspend:
|
|
case IrInstructionIdCoroFree:
|
|
case IrInstructionIdCoroPromise:
|
|
case IrInstructionIdPromiseResultType:
|
|
case IrInstructionIdSqrt:
|
|
case IrInstructionIdBswap:
|
|
case IrInstructionIdBitReverse:
|
|
case IrInstructionIdAtomicLoad:
|
|
case IrInstructionIdIntCast:
|
|
case IrInstructionIdFloatCast:
|
|
case IrInstructionIdErrSetCast:
|
|
case IrInstructionIdIntToFloat:
|
|
case IrInstructionIdFloatToInt:
|
|
case IrInstructionIdBoolToInt:
|
|
case IrInstructionIdFromBytes:
|
|
case IrInstructionIdToBytes:
|
|
case IrInstructionIdEnumToInt:
|
|
case IrInstructionIdVectorToArray:
|
|
case IrInstructionIdArrayToVector:
|
|
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;
|
|
}
|
|
}
|
|
zig_unreachable();
|
|
}
|