Files
zig/src/stage1/astgen.cpp
Andrew Kelley b11ac9c5bf stage1: move some mutable state from Stage1Zir to IrAnalyze
This is progress towards making Stage1Zir immutable, so that we can
avoid generating it for every comptime function call.

Also rename IrExecutableGen to Stage1Air.
2021-05-28 12:58:40 -07:00

8118 lines
354 KiB
C++

/*
* Copyright (c) 2021 Andrew Kelley
*
* This file is part of zig, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#include "astgen.hpp"
#include "analyze.hpp"
#include "util.hpp"
#include "os.hpp"
#include "parser.hpp"
struct IrBuilderSrc {
CodeGen *codegen;
Stage1Zir *exec;
IrBasicBlockSrc *current_basic_block;
AstNode *main_block_node;
};
static IrInstSrc *ir_gen_node(IrBuilderSrc *irb, AstNode *node, Scope *scope);
static IrInstSrc *ir_gen_node_extra(IrBuilderSrc *irb, AstNode *node, Scope *scope, LVal lval,
ResultLoc *result_loc);
static IrInstSrc *ir_lval_wrap(IrBuilderSrc *irb, Scope *scope, IrInstSrc *value, LVal lval,
ResultLoc *result_loc);
static IrInstSrc *ir_expr_wrap(IrBuilderSrc *irb, Scope *scope, IrInstSrc *inst,
ResultLoc *result_loc);
static IrInstSrc *ir_gen_union_init_expr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *union_type, IrInstSrc *field_name, AstNode *expr_node,
LVal lval, ResultLoc *parent_result_loc);
static ResultLocCast *ir_build_cast_result_loc(IrBuilderSrc *irb, IrInstSrc *dest_type,
ResultLoc *parent_result_loc);
static ZigVar *ir_create_var(IrBuilderSrc *irb, AstNode *node, Scope *scope, Buf *name,
bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstSrc *is_comptime);
static void build_decl_var_and_init(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
ZigVar *var, IrInstSrc *init, const char *name_hint, IrInstSrc *is_comptime);
static void ir_assert_impl(bool ok, IrInst *source_instruction, char const *file, unsigned int line) {
if (ok) return;
src_assert_impl(ok, source_instruction->source_node, file, line);
}
static ErrorMsg *exec_add_error_node(CodeGen *codegen, Stage1Zir *exec, AstNode *source_node, Buf *msg) {
ErrorMsg *err_msg = add_node_error(codegen, source_node, msg);
invalidate_exec(exec, err_msg);
return err_msg;
}
#define ir_assert(OK, SOURCE_INSTRUCTION) ir_assert_impl((OK), (SOURCE_INSTRUCTION), __FILE__, __LINE__)
static bool instr_is_unreachable(IrInstSrc *instruction) {
return instruction->is_noreturn;
}
void destroy_instruction_src(IrInstSrc *inst) {
switch (inst->id) {
case IrInstSrcIdInvalid:
zig_unreachable();
case IrInstSrcIdReturn:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcReturn *>(inst));
case IrInstSrcIdConst:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcConst *>(inst));
case IrInstSrcIdBinOp:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBinOp *>(inst));
case IrInstSrcIdMergeErrSets:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcMergeErrSets *>(inst));
case IrInstSrcIdDeclVar:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcDeclVar *>(inst));
case IrInstSrcIdCall:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCall *>(inst));
case IrInstSrcIdCallExtra:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCallExtra *>(inst));
case IrInstSrcIdAsyncCallExtra:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAsyncCallExtra *>(inst));
case IrInstSrcIdUnOp:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcUnOp *>(inst));
case IrInstSrcIdCondBr:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCondBr *>(inst));
case IrInstSrcIdBr:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBr *>(inst));
case IrInstSrcIdPhi:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPhi *>(inst));
case IrInstSrcIdContainerInitList:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcContainerInitList *>(inst));
case IrInstSrcIdContainerInitFields:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcContainerInitFields *>(inst));
case IrInstSrcIdUnreachable:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcUnreachable *>(inst));
case IrInstSrcIdElemPtr:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcElemPtr *>(inst));
case IrInstSrcIdVarPtr:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcVarPtr *>(inst));
case IrInstSrcIdLoadPtr:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcLoadPtr *>(inst));
case IrInstSrcIdStorePtr:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcStorePtr *>(inst));
case IrInstSrcIdTypeOf:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTypeOf *>(inst));
case IrInstSrcIdFieldPtr:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFieldPtr *>(inst));
case IrInstSrcIdSetCold:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSetCold *>(inst));
case IrInstSrcIdSetRuntimeSafety:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSetRuntimeSafety *>(inst));
case IrInstSrcIdSetFloatMode:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSetFloatMode *>(inst));
case IrInstSrcIdArrayType:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcArrayType *>(inst));
case IrInstSrcIdSliceType:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSliceType *>(inst));
case IrInstSrcIdAnyFrameType:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAnyFrameType *>(inst));
case IrInstSrcIdAsm:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAsm *>(inst));
case IrInstSrcIdSizeOf:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSizeOf *>(inst));
case IrInstSrcIdTestNonNull:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTestNonNull *>(inst));
case IrInstSrcIdOptionalUnwrapPtr:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcOptionalUnwrapPtr *>(inst));
case IrInstSrcIdPopCount:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPopCount *>(inst));
case IrInstSrcIdClz:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcClz *>(inst));
case IrInstSrcIdCtz:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCtz *>(inst));
case IrInstSrcIdBswap:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBswap *>(inst));
case IrInstSrcIdBitReverse:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBitReverse *>(inst));
case IrInstSrcIdSwitchBr:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSwitchBr *>(inst));
case IrInstSrcIdSwitchVar:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSwitchVar *>(inst));
case IrInstSrcIdSwitchElseVar:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSwitchElseVar *>(inst));
case IrInstSrcIdSwitchTarget:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSwitchTarget *>(inst));
case IrInstSrcIdImport:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcImport *>(inst));
case IrInstSrcIdRef:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcRef *>(inst));
case IrInstSrcIdCompileErr:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCompileErr *>(inst));
case IrInstSrcIdCompileLog:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCompileLog *>(inst));
case IrInstSrcIdErrName:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcErrName *>(inst));
case IrInstSrcIdCImport:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCImport *>(inst));
case IrInstSrcIdCInclude:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCInclude *>(inst));
case IrInstSrcIdCDefine:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCDefine *>(inst));
case IrInstSrcIdCUndef:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCUndef *>(inst));
case IrInstSrcIdEmbedFile:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcEmbedFile *>(inst));
case IrInstSrcIdCmpxchg:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCmpxchg *>(inst));
case IrInstSrcIdFence:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFence *>(inst));
case IrInstSrcIdReduce:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcReduce *>(inst));
case IrInstSrcIdTruncate:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTruncate *>(inst));
case IrInstSrcIdIntCast:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcIntCast *>(inst));
case IrInstSrcIdFloatCast:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFloatCast *>(inst));
case IrInstSrcIdErrSetCast:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcErrSetCast *>(inst));
case IrInstSrcIdIntToFloat:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcIntToFloat *>(inst));
case IrInstSrcIdFloatToInt:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFloatToInt *>(inst));
case IrInstSrcIdBoolToInt:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBoolToInt *>(inst));
case IrInstSrcIdVectorType:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcVectorType *>(inst));
case IrInstSrcIdShuffleVector:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcShuffleVector *>(inst));
case IrInstSrcIdSplat:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSplat *>(inst));
case IrInstSrcIdBoolNot:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBoolNot *>(inst));
case IrInstSrcIdMemset:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcMemset *>(inst));
case IrInstSrcIdMemcpy:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcMemcpy *>(inst));
case IrInstSrcIdSlice:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSlice *>(inst));
case IrInstSrcIdBreakpoint:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBreakpoint *>(inst));
case IrInstSrcIdReturnAddress:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcReturnAddress *>(inst));
case IrInstSrcIdFrameAddress:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFrameAddress *>(inst));
case IrInstSrcIdFrameHandle:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFrameHandle *>(inst));
case IrInstSrcIdFrameType:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFrameType *>(inst));
case IrInstSrcIdFrameSize:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFrameSize *>(inst));
case IrInstSrcIdAlignOf:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAlignOf *>(inst));
case IrInstSrcIdOverflowOp:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcOverflowOp *>(inst));
case IrInstSrcIdTestErr:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTestErr *>(inst));
case IrInstSrcIdUnwrapErrCode:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcUnwrapErrCode *>(inst));
case IrInstSrcIdUnwrapErrPayload:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcUnwrapErrPayload *>(inst));
case IrInstSrcIdFnProto:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFnProto *>(inst));
case IrInstSrcIdTestComptime:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTestComptime *>(inst));
case IrInstSrcIdPtrCast:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPtrCast *>(inst));
case IrInstSrcIdBitCast:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBitCast *>(inst));
case IrInstSrcIdPtrToInt:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPtrToInt *>(inst));
case IrInstSrcIdIntToPtr:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcIntToPtr *>(inst));
case IrInstSrcIdIntToEnum:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcIntToEnum *>(inst));
case IrInstSrcIdIntToErr:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcIntToErr *>(inst));
case IrInstSrcIdErrToInt:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcErrToInt *>(inst));
case IrInstSrcIdCheckSwitchProngsUnderNo:
case IrInstSrcIdCheckSwitchProngsUnderYes:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCheckSwitchProngs *>(inst));
case IrInstSrcIdCheckStatementIsVoid:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCheckStatementIsVoid *>(inst));
case IrInstSrcIdTypeName:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTypeName *>(inst));
case IrInstSrcIdTagName:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTagName *>(inst));
case IrInstSrcIdPtrType:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPtrType *>(inst));
case IrInstSrcIdPtrTypeSimple:
case IrInstSrcIdPtrTypeSimpleConst:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPtrTypeSimple *>(inst));
case IrInstSrcIdDeclRef:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcDeclRef *>(inst));
case IrInstSrcIdPanic:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcPanic *>(inst));
case IrInstSrcIdFieldParentPtr:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFieldParentPtr *>(inst));
case IrInstSrcIdByteOffsetOf:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcByteOffsetOf *>(inst));
case IrInstSrcIdBitOffsetOf:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcBitOffsetOf *>(inst));
case IrInstSrcIdTypeInfo:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTypeInfo *>(inst));
case IrInstSrcIdType:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcType *>(inst));
case IrInstSrcIdHasField:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcHasField *>(inst));
case IrInstSrcIdSetEvalBranchQuota:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSetEvalBranchQuota *>(inst));
case IrInstSrcIdAlignCast:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAlignCast *>(inst));
case IrInstSrcIdImplicitCast:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcImplicitCast *>(inst));
case IrInstSrcIdResolveResult:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcResolveResult *>(inst));
case IrInstSrcIdResetResult:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcResetResult *>(inst));
case IrInstSrcIdSetAlignStack:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSetAlignStack *>(inst));
case IrInstSrcIdArgTypeAllowVarFalse:
case IrInstSrcIdArgTypeAllowVarTrue:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcArgType *>(inst));
case IrInstSrcIdExport:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcExport *>(inst));
case IrInstSrcIdExtern:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcExtern *>(inst));
case IrInstSrcIdErrorReturnTrace:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcErrorReturnTrace *>(inst));
case IrInstSrcIdErrorUnion:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcErrorUnion *>(inst));
case IrInstSrcIdAtomicRmw:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAtomicRmw *>(inst));
case IrInstSrcIdSaveErrRetAddr:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSaveErrRetAddr *>(inst));
case IrInstSrcIdAddImplicitReturnType:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAddImplicitReturnType *>(inst));
case IrInstSrcIdFloatOp:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcFloatOp *>(inst));
case IrInstSrcIdMulAdd:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcMulAdd *>(inst));
case IrInstSrcIdAtomicLoad:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAtomicLoad *>(inst));
case IrInstSrcIdAtomicStore:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAtomicStore *>(inst));
case IrInstSrcIdEnumToInt:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcEnumToInt *>(inst));
case IrInstSrcIdCheckRuntimeScope:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCheckRuntimeScope *>(inst));
case IrInstSrcIdHasDecl:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcHasDecl *>(inst));
case IrInstSrcIdUndeclaredIdent:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcUndeclaredIdent *>(inst));
case IrInstSrcIdAlloca:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAlloca *>(inst));
case IrInstSrcIdEndExpr:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcEndExpr *>(inst));
case IrInstSrcIdUnionInitNamedField:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcUnionInitNamedField *>(inst));
case IrInstSrcIdSuspendBegin:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSuspendBegin *>(inst));
case IrInstSrcIdSuspendFinish:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSuspendFinish *>(inst));
case IrInstSrcIdResume:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcResume *>(inst));
case IrInstSrcIdAwait:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcAwait *>(inst));
case IrInstSrcIdSpillBegin:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSpillBegin *>(inst));
case IrInstSrcIdSpillEnd:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSpillEnd *>(inst));
case IrInstSrcIdCallArgs:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcCallArgs *>(inst));
case IrInstSrcIdWasmMemorySize:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcWasmMemorySize *>(inst));
case IrInstSrcIdWasmMemoryGrow:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcWasmMemoryGrow *>(inst));
case IrInstSrcIdSrc:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcSrc *>(inst));
}
zig_unreachable();
}
bool ir_should_inline(Stage1Zir *exec, Scope *scope) {
if (exec->is_inline)
return true;
while (scope != nullptr) {
if (scope->id == ScopeIdCompTime)
return true;
if (scope->id == ScopeIdTypeOf)
return false;
if (scope->id == ScopeIdFnDef)
break;
scope = scope->parent;
}
return false;
}
static void ir_instruction_append(IrBasicBlockSrc *basic_block, IrInstSrc *instruction) {
assert(basic_block);
assert(instruction);
basic_block->instruction_list.append(instruction);
}
static size_t exec_next_debug_id(Stage1Zir *exec) {
size_t result = exec->next_debug_id;
exec->next_debug_id += 1;
return result;
}
static ZigFn *exec_fn_entry(Stage1Zir *exec) {
return exec->fn_entry;
}
static Buf *exec_c_import_buf(Stage1Zir *exec) {
return exec->c_import_buf;
}
static void ir_ref_bb(IrBasicBlockSrc *bb) {
bb->ref_count += 1;
}
static void ir_ref_instruction(IrInstSrc *instruction, IrBasicBlockSrc *cur_bb) {
assert(instruction->id != IrInstSrcIdInvalid);
instruction->base.ref_count += 1;
if (instruction->owner_bb != cur_bb && !instr_is_unreachable(instruction)
&& instruction->id != IrInstSrcIdConst)
{
ir_ref_bb(instruction->owner_bb);
}
}
static IrBasicBlockSrc *ir_create_basic_block(IrBuilderSrc *irb, Scope *scope, const char *name_hint) {
IrBasicBlockSrc *result = heap::c_allocator.create<IrBasicBlockSrc>();
result->scope = scope;
result->name_hint = name_hint;
result->debug_id = exec_next_debug_id(irb->exec);
result->index = UINT32_MAX; // set later
return result;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcDeclVar *) {
return IrInstSrcIdDeclVar;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcBr *) {
return IrInstSrcIdBr;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcCondBr *) {
return IrInstSrcIdCondBr;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcSwitchBr *) {
return IrInstSrcIdSwitchBr;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcSwitchVar *) {
return IrInstSrcIdSwitchVar;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcSwitchElseVar *) {
return IrInstSrcIdSwitchElseVar;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcSwitchTarget *) {
return IrInstSrcIdSwitchTarget;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcPhi *) {
return IrInstSrcIdPhi;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcUnOp *) {
return IrInstSrcIdUnOp;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcBinOp *) {
return IrInstSrcIdBinOp;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcMergeErrSets *) {
return IrInstSrcIdMergeErrSets;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcLoadPtr *) {
return IrInstSrcIdLoadPtr;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcStorePtr *) {
return IrInstSrcIdStorePtr;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcFieldPtr *) {
return IrInstSrcIdFieldPtr;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcElemPtr *) {
return IrInstSrcIdElemPtr;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcVarPtr *) {
return IrInstSrcIdVarPtr;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcCall *) {
return IrInstSrcIdCall;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcCallArgs *) {
return IrInstSrcIdCallArgs;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcCallExtra *) {
return IrInstSrcIdCallExtra;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcAsyncCallExtra *) {
return IrInstSrcIdAsyncCallExtra;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcConst *) {
return IrInstSrcIdConst;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcReturn *) {
return IrInstSrcIdReturn;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcContainerInitList *) {
return IrInstSrcIdContainerInitList;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcContainerInitFields *) {
return IrInstSrcIdContainerInitFields;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcUnreachable *) {
return IrInstSrcIdUnreachable;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcTypeOf *) {
return IrInstSrcIdTypeOf;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetCold *) {
return IrInstSrcIdSetCold;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetRuntimeSafety *) {
return IrInstSrcIdSetRuntimeSafety;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetFloatMode *) {
return IrInstSrcIdSetFloatMode;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcArrayType *) {
return IrInstSrcIdArrayType;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcAnyFrameType *) {
return IrInstSrcIdAnyFrameType;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcSliceType *) {
return IrInstSrcIdSliceType;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcAsm *) {
return IrInstSrcIdAsm;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcSizeOf *) {
return IrInstSrcIdSizeOf;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcTestNonNull *) {
return IrInstSrcIdTestNonNull;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcOptionalUnwrapPtr *) {
return IrInstSrcIdOptionalUnwrapPtr;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcClz *) {
return IrInstSrcIdClz;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcCtz *) {
return IrInstSrcIdCtz;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcPopCount *) {
return IrInstSrcIdPopCount;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcBswap *) {
return IrInstSrcIdBswap;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcBitReverse *) {
return IrInstSrcIdBitReverse;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcImport *) {
return IrInstSrcIdImport;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcCImport *) {
return IrInstSrcIdCImport;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcCInclude *) {
return IrInstSrcIdCInclude;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcCDefine *) {
return IrInstSrcIdCDefine;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcCUndef *) {
return IrInstSrcIdCUndef;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcRef *) {
return IrInstSrcIdRef;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcCompileErr *) {
return IrInstSrcIdCompileErr;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcCompileLog *) {
return IrInstSrcIdCompileLog;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrName *) {
return IrInstSrcIdErrName;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcEmbedFile *) {
return IrInstSrcIdEmbedFile;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcCmpxchg *) {
return IrInstSrcIdCmpxchg;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcFence *) {
return IrInstSrcIdFence;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcReduce *) {
return IrInstSrcIdReduce;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcTruncate *) {
return IrInstSrcIdTruncate;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcIntCast *) {
return IrInstSrcIdIntCast;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcFloatCast *) {
return IrInstSrcIdFloatCast;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcIntToFloat *) {
return IrInstSrcIdIntToFloat;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcFloatToInt *) {
return IrInstSrcIdFloatToInt;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcBoolToInt *) {
return IrInstSrcIdBoolToInt;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcVectorType *) {
return IrInstSrcIdVectorType;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcShuffleVector *) {
return IrInstSrcIdShuffleVector;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcSplat *) {
return IrInstSrcIdSplat;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcBoolNot *) {
return IrInstSrcIdBoolNot;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcMemset *) {
return IrInstSrcIdMemset;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcMemcpy *) {
return IrInstSrcIdMemcpy;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcSlice *) {
return IrInstSrcIdSlice;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcBreakpoint *) {
return IrInstSrcIdBreakpoint;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcReturnAddress *) {
return IrInstSrcIdReturnAddress;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcFrameAddress *) {
return IrInstSrcIdFrameAddress;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcFrameHandle *) {
return IrInstSrcIdFrameHandle;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcFrameType *) {
return IrInstSrcIdFrameType;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcFrameSize *) {
return IrInstSrcIdFrameSize;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcAlignOf *) {
return IrInstSrcIdAlignOf;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcOverflowOp *) {
return IrInstSrcIdOverflowOp;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcTestErr *) {
return IrInstSrcIdTestErr;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcMulAdd *) {
return IrInstSrcIdMulAdd;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcFloatOp *) {
return IrInstSrcIdFloatOp;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcUnwrapErrCode *) {
return IrInstSrcIdUnwrapErrCode;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcUnwrapErrPayload *) {
return IrInstSrcIdUnwrapErrPayload;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcFnProto *) {
return IrInstSrcIdFnProto;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcTestComptime *) {
return IrInstSrcIdTestComptime;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcPtrCast *) {
return IrInstSrcIdPtrCast;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcBitCast *) {
return IrInstSrcIdBitCast;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcIntToPtr *) {
return IrInstSrcIdIntToPtr;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcPtrToInt *) {
return IrInstSrcIdPtrToInt;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcIntToEnum *) {
return IrInstSrcIdIntToEnum;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcEnumToInt *) {
return IrInstSrcIdEnumToInt;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcIntToErr *) {
return IrInstSrcIdIntToErr;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrToInt *) {
return IrInstSrcIdErrToInt;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcCheckStatementIsVoid *) {
return IrInstSrcIdCheckStatementIsVoid;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcTypeName *) {
return IrInstSrcIdTypeName;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcDeclRef *) {
return IrInstSrcIdDeclRef;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcPanic *) {
return IrInstSrcIdPanic;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcTagName *) {
return IrInstSrcIdTagName;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcFieldParentPtr *) {
return IrInstSrcIdFieldParentPtr;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcByteOffsetOf *) {
return IrInstSrcIdByteOffsetOf;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcBitOffsetOf *) {
return IrInstSrcIdBitOffsetOf;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcTypeInfo *) {
return IrInstSrcIdTypeInfo;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcType *) {
return IrInstSrcIdType;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcHasField *) {
return IrInstSrcIdHasField;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetEvalBranchQuota *) {
return IrInstSrcIdSetEvalBranchQuota;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcPtrType *) {
return IrInstSrcIdPtrType;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcAlignCast *) {
return IrInstSrcIdAlignCast;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcImplicitCast *) {
return IrInstSrcIdImplicitCast;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcResolveResult *) {
return IrInstSrcIdResolveResult;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcResetResult *) {
return IrInstSrcIdResetResult;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetAlignStack *) {
return IrInstSrcIdSetAlignStack;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcExport *) {
return IrInstSrcIdExport;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcExtern *) {
return IrInstSrcIdExtern;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrorReturnTrace *) {
return IrInstSrcIdErrorReturnTrace;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrorUnion *) {
return IrInstSrcIdErrorUnion;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcAtomicRmw *) {
return IrInstSrcIdAtomicRmw;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcAtomicLoad *) {
return IrInstSrcIdAtomicLoad;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcAtomicStore *) {
return IrInstSrcIdAtomicStore;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcSaveErrRetAddr *) {
return IrInstSrcIdSaveErrRetAddr;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcAddImplicitReturnType *) {
return IrInstSrcIdAddImplicitReturnType;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrSetCast *) {
return IrInstSrcIdErrSetCast;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcCheckRuntimeScope *) {
return IrInstSrcIdCheckRuntimeScope;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcHasDecl *) {
return IrInstSrcIdHasDecl;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcUndeclaredIdent *) {
return IrInstSrcIdUndeclaredIdent;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcAlloca *) {
return IrInstSrcIdAlloca;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcEndExpr *) {
return IrInstSrcIdEndExpr;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcUnionInitNamedField *) {
return IrInstSrcIdUnionInitNamedField;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcSuspendBegin *) {
return IrInstSrcIdSuspendBegin;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcSuspendFinish *) {
return IrInstSrcIdSuspendFinish;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcAwait *) {
return IrInstSrcIdAwait;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcResume *) {
return IrInstSrcIdResume;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcSpillBegin *) {
return IrInstSrcIdSpillBegin;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcSpillEnd *) {
return IrInstSrcIdSpillEnd;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcWasmMemorySize *) {
return IrInstSrcIdWasmMemorySize;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcWasmMemoryGrow *) {
return IrInstSrcIdWasmMemoryGrow;
}
static constexpr IrInstSrcId ir_inst_id(IrInstSrcSrc *) {
return IrInstSrcIdSrc;
}
template<typename T>
static T *ir_create_instruction(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
T *special_instruction = heap::c_allocator.create<T>();
special_instruction->base.id = ir_inst_id(special_instruction);
special_instruction->base.base.scope = scope;
special_instruction->base.base.source_node = source_node;
special_instruction->base.base.debug_id = exec_next_debug_id(irb->exec);
special_instruction->base.owner_bb = irb->current_basic_block;
return special_instruction;
}
template<typename T>
static T *ir_build_instruction(IrBuilderSrc *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 IrInstSrc *ir_build_cond_br(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *condition,
IrBasicBlockSrc *then_block, IrBasicBlockSrc *else_block, IrInstSrc *is_comptime)
{
IrInstSrcCondBr *inst = ir_build_instruction<IrInstSrcCondBr>(irb, scope, source_node);
inst->base.is_noreturn = true;
inst->condition = condition;
inst->then_block = then_block;
inst->else_block = else_block;
inst->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 &inst->base;
}
static IrInstSrc *ir_build_return_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *operand) {
IrInstSrcReturn *inst = ir_build_instruction<IrInstSrcReturn>(irb, scope, source_node);
inst->base.is_noreturn = true;
inst->operand = operand;
if (operand != nullptr) ir_ref_instruction(operand, irb->current_basic_block);
return &inst->base;
}
static IrInstSrc *ir_build_const_void(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
IrInstSrcConst *const_instruction = ir_create_instruction<IrInstSrcConst>(irb, scope, source_node);
ir_instruction_append(irb->current_basic_block, &const_instruction->base);
const_instruction->value = irb->codegen->intern.for_void();
return &const_instruction->base;
}
static IrInstSrc *ir_build_const_undefined(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
IrInstSrcConst *const_instruction = ir_create_instruction<IrInstSrcConst>(irb, scope, source_node);
ir_instruction_append(irb->current_basic_block, &const_instruction->base);
const_instruction->value = irb->codegen->intern.for_undefined();
const_instruction->value->special = ConstValSpecialUndef;
return &const_instruction->base;
}
static IrInstSrc *ir_build_const_uint(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, uint64_t value) {
IrInstSrcConst *const_instruction = ir_build_instruction<IrInstSrcConst>(irb, scope, source_node);
const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
const_instruction->value->type = irb->codegen->builtin_types.entry_num_lit_int;
const_instruction->value->special = ConstValSpecialStatic;
bigint_init_unsigned(&const_instruction->value->data.x_bigint, value);
return &const_instruction->base;
}
static IrInstSrc *ir_build_const_bigint(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
BigInt bigint)
{
IrInstSrcConst *const_instruction = ir_build_instruction<IrInstSrcConst>(irb, scope, source_node);
const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
const_instruction->value->type = irb->codegen->builtin_types.entry_num_lit_int;
const_instruction->value->special = ConstValSpecialStatic;
const_instruction->value->data.x_bigint = bigint;
return &const_instruction->base;
}
static IrInstSrc *ir_build_const_bigfloat(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
BigFloat bigfloat)
{
IrInstSrcConst *const_instruction = ir_build_instruction<IrInstSrcConst>(irb, scope, source_node);
const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
const_instruction->value->type = irb->codegen->builtin_types.entry_num_lit_float;
const_instruction->value->special = ConstValSpecialStatic;
const_instruction->value->data.x_bigfloat = bigfloat;
return &const_instruction->base;
}
static IrInstSrc *ir_build_const_null(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
IrInstSrcConst *const_instruction = ir_create_instruction<IrInstSrcConst>(irb, scope, source_node);
ir_instruction_append(irb->current_basic_block, &const_instruction->base);
const_instruction->value = irb->codegen->intern.for_null();
return &const_instruction->base;
}
static IrInstSrc *ir_build_const_usize(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, uint64_t value) {
IrInstSrcConst *const_instruction = ir_build_instruction<IrInstSrcConst>(irb, scope, source_node);
const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
const_instruction->value->type = irb->codegen->builtin_types.entry_usize;
const_instruction->value->special = ConstValSpecialStatic;
bigint_init_unsigned(&const_instruction->value->data.x_bigint, value);
return &const_instruction->base;
}
static IrInstSrc *ir_create_const_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
ZigType *type_entry)
{
IrInstSrcConst *const_instruction = ir_create_instruction<IrInstSrcConst>(irb, scope, source_node);
const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
const_instruction->value->type = irb->codegen->builtin_types.entry_type;
const_instruction->value->special = ConstValSpecialStatic;
const_instruction->value->data.x_type = type_entry;
return &const_instruction->base;
}
static IrInstSrc *ir_build_const_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
ZigType *type_entry)
{
IrInstSrc *instruction = ir_create_const_type(irb, scope, source_node, type_entry);
ir_instruction_append(irb->current_basic_block, instruction);
return instruction;
}
static IrInstSrc *ir_build_const_import(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigType *import) {
IrInstSrcConst *const_instruction = ir_build_instruction<IrInstSrcConst>(irb, scope, source_node);
const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
const_instruction->value->type = irb->codegen->builtin_types.entry_type;
const_instruction->value->special = ConstValSpecialStatic;
const_instruction->value->data.x_type = import;
return &const_instruction->base;
}
static IrInstSrc *ir_build_const_bool(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, bool value) {
IrInstSrcConst *const_instruction = ir_build_instruction<IrInstSrcConst>(irb, scope, source_node);
const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
const_instruction->value->type = irb->codegen->builtin_types.entry_bool;
const_instruction->value->special = ConstValSpecialStatic;
const_instruction->value->data.x_bool = value;
return &const_instruction->base;
}
static IrInstSrc *ir_build_const_enum_literal(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, Buf *name) {
IrInstSrcConst *const_instruction = ir_build_instruction<IrInstSrcConst>(irb, scope, source_node);
const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
const_instruction->value->type = irb->codegen->builtin_types.entry_enum_literal;
const_instruction->value->special = ConstValSpecialStatic;
const_instruction->value->data.x_enum_literal = name;
return &const_instruction->base;
}
// Consumes `str`.
static IrInstSrc *ir_create_const_str_lit(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, Buf *str) {
IrInstSrcConst *const_instruction = ir_create_instruction<IrInstSrcConst>(irb, scope, source_node);
const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
init_const_str_lit(irb->codegen, const_instruction->value, str, true);
return &const_instruction->base;
}
// Consumes `str`.
static IrInstSrc *ir_build_const_str_lit(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, Buf *str) {
IrInstSrc *instruction = ir_create_const_str_lit(irb, scope, source_node, str);
ir_instruction_append(irb->current_basic_block, instruction);
return instruction;
}
static IrInstSrc *ir_build_bin_op(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrBinOp op_id,
IrInstSrc *op1, IrInstSrc *op2, bool safety_check_on)
{
IrInstSrcBinOp *inst = ir_build_instruction<IrInstSrcBinOp>(irb, scope, source_node);
inst->op_id = op_id;
inst->op1 = op1;
inst->op2 = op2;
inst->safety_check_on = safety_check_on;
ir_ref_instruction(op1, irb->current_basic_block);
ir_ref_instruction(op2, irb->current_basic_block);
return &inst->base;
}
static IrInstSrc *ir_build_merge_err_sets(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *op1, IrInstSrc *op2, Buf *type_name)
{
IrInstSrcMergeErrSets *inst = ir_build_instruction<IrInstSrcMergeErrSets>(irb, scope, source_node);
inst->op1 = op1;
inst->op2 = op2;
inst->type_name = type_name;
ir_ref_instruction(op1, irb->current_basic_block);
ir_ref_instruction(op2, irb->current_basic_block);
return &inst->base;
}
static IrInstSrc *ir_build_var_ptr_x(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigVar *var,
ScopeFnDef *crossed_fndef_scope)
{
IrInstSrcVarPtr *instruction = ir_build_instruction<IrInstSrcVarPtr>(irb, scope, source_node);
instruction->var = var;
instruction->crossed_fndef_scope = crossed_fndef_scope;
var->ref_count += 1;
return &instruction->base;
}
static IrInstSrc *ir_build_var_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigVar *var) {
return ir_build_var_ptr_x(irb, scope, source_node, var, nullptr);
}
static IrInstSrc *ir_build_elem_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *array_ptr, IrInstSrc *elem_index, bool safety_check_on, PtrLen ptr_len,
AstNode *init_array_type_source_node)
{
IrInstSrcElemPtr *instruction = ir_build_instruction<IrInstSrcElemPtr>(irb, scope, source_node);
instruction->array_ptr = array_ptr;
instruction->elem_index = elem_index;
instruction->safety_check_on = safety_check_on;
instruction->ptr_len = ptr_len;
instruction->init_array_type_source_node = init_array_type_source_node;
ir_ref_instruction(array_ptr, irb->current_basic_block);
ir_ref_instruction(elem_index, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_field_ptr_instruction(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *container_ptr, IrInstSrc *field_name_expr, bool initializing)
{
IrInstSrcFieldPtr *instruction = ir_build_instruction<IrInstSrcFieldPtr>(irb, scope, source_node);
instruction->container_ptr = container_ptr;
instruction->field_name_buffer = nullptr;
instruction->field_name_expr = field_name_expr;
instruction->initializing = initializing;
ir_ref_instruction(container_ptr, irb->current_basic_block);
ir_ref_instruction(field_name_expr, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_field_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *container_ptr, Buf *field_name, bool initializing)
{
IrInstSrcFieldPtr *instruction = ir_build_instruction<IrInstSrcFieldPtr>(irb, scope, source_node);
instruction->container_ptr = container_ptr;
instruction->field_name_buffer = field_name;
instruction->field_name_expr = nullptr;
instruction->initializing = initializing;
ir_ref_instruction(container_ptr, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_has_field(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *container_type, IrInstSrc *field_name)
{
IrInstSrcHasField *instruction = ir_build_instruction<IrInstSrcHasField>(irb, scope, source_node);
instruction->container_type = container_type;
instruction->field_name = field_name;
ir_ref_instruction(container_type, irb->current_basic_block);
ir_ref_instruction(field_name, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_call_extra(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *options, IrInstSrc *fn_ref, IrInstSrc *args, ResultLoc *result_loc)
{
IrInstSrcCallExtra *call_instruction = ir_build_instruction<IrInstSrcCallExtra>(irb, scope, source_node);
call_instruction->options = options;
call_instruction->fn_ref = fn_ref;
call_instruction->args = args;
call_instruction->result_loc = result_loc;
ir_ref_instruction(options, irb->current_basic_block);
ir_ref_instruction(fn_ref, irb->current_basic_block);
ir_ref_instruction(args, irb->current_basic_block);
return &call_instruction->base;
}
static IrInstSrc *ir_build_async_call_extra(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
CallModifier modifier, IrInstSrc *fn_ref, IrInstSrc *ret_ptr, IrInstSrc *new_stack, IrInstSrc *args, ResultLoc *result_loc)
{
IrInstSrcAsyncCallExtra *call_instruction = ir_build_instruction<IrInstSrcAsyncCallExtra>(irb, scope, source_node);
call_instruction->modifier = modifier;
call_instruction->fn_ref = fn_ref;
call_instruction->ret_ptr = ret_ptr;
call_instruction->new_stack = new_stack;
call_instruction->args = args;
call_instruction->result_loc = result_loc;
ir_ref_instruction(fn_ref, irb->current_basic_block);
if (ret_ptr != nullptr) ir_ref_instruction(ret_ptr, irb->current_basic_block);
ir_ref_instruction(new_stack, irb->current_basic_block);
ir_ref_instruction(args, irb->current_basic_block);
return &call_instruction->base;
}
static IrInstSrc *ir_build_call_args(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *options, IrInstSrc *fn_ref, IrInstSrc **args_ptr, size_t args_len,
ResultLoc *result_loc)
{
IrInstSrcCallArgs *call_instruction = ir_build_instruction<IrInstSrcCallArgs>(irb, scope, source_node);
call_instruction->options = options;
call_instruction->fn_ref = fn_ref;
call_instruction->args_ptr = args_ptr;
call_instruction->args_len = args_len;
call_instruction->result_loc = result_loc;
ir_ref_instruction(options, irb->current_basic_block);
ir_ref_instruction(fn_ref, irb->current_basic_block);
for (size_t i = 0; i < args_len; i += 1)
ir_ref_instruction(args_ptr[i], irb->current_basic_block);
return &call_instruction->base;
}
static IrInstSrc *ir_build_call_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
ZigFn *fn_entry, IrInstSrc *fn_ref, size_t arg_count, IrInstSrc **args,
IrInstSrc *ret_ptr, CallModifier modifier, bool is_async_call_builtin,
IrInstSrc *new_stack, ResultLoc *result_loc)
{
IrInstSrcCall *call_instruction = ir_build_instruction<IrInstSrcCall>(irb, scope, source_node);
call_instruction->fn_entry = fn_entry;
call_instruction->fn_ref = fn_ref;
call_instruction->args = args;
call_instruction->arg_count = arg_count;
call_instruction->modifier = modifier;
call_instruction->is_async_call_builtin = is_async_call_builtin;
call_instruction->new_stack = new_stack;
call_instruction->result_loc = result_loc;
call_instruction->ret_ptr = ret_ptr;
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 (ret_ptr != nullptr) ir_ref_instruction(ret_ptr, irb->current_basic_block);
if (new_stack != nullptr) ir_ref_instruction(new_stack, irb->current_basic_block);
return &call_instruction->base;
}
static IrInstSrc *ir_build_phi(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
size_t incoming_count, IrBasicBlockSrc **incoming_blocks, IrInstSrc **incoming_values,
ResultLocPeerParent *peer_parent)
{
assert(incoming_count != 0);
assert(incoming_count != SIZE_MAX);
IrInstSrcPhi *phi_instruction = ir_build_instruction<IrInstSrcPhi>(irb, scope, source_node);
phi_instruction->incoming_count = incoming_count;
phi_instruction->incoming_blocks = incoming_blocks;
phi_instruction->incoming_values = incoming_values;
phi_instruction->peer_parent = peer_parent;
for (size_t i = 0; i < incoming_count; i += 1) {
ir_ref_bb(incoming_blocks[i]);
ir_ref_instruction(incoming_values[i], irb->current_basic_block);
}
return &phi_instruction->base;
}
static IrInstSrc *ir_build_br(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrBasicBlockSrc *dest_block, IrInstSrc *is_comptime)
{
IrInstSrcBr *inst = ir_build_instruction<IrInstSrcBr>(irb, scope, source_node);
inst->base.is_noreturn = true;
inst->dest_block = dest_block;
inst->is_comptime = is_comptime;
ir_ref_bb(dest_block);
if (is_comptime) ir_ref_instruction(is_comptime, irb->current_basic_block);
return &inst->base;
}
static IrInstSrc *ir_build_ptr_type_simple(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *child_type, bool is_const)
{
IrInstSrcPtrTypeSimple *inst = heap::c_allocator.create<IrInstSrcPtrTypeSimple>();
inst->base.id = is_const ? IrInstSrcIdPtrTypeSimpleConst : IrInstSrcIdPtrTypeSimple;
inst->base.base.scope = scope;
inst->base.base.source_node = source_node;
inst->base.base.debug_id = exec_next_debug_id(irb->exec);
inst->base.owner_bb = irb->current_basic_block;
ir_instruction_append(irb->current_basic_block, &inst->base);
inst->child_type = child_type;
ir_ref_instruction(child_type, irb->current_basic_block);
return &inst->base;
}
static IrInstSrc *ir_build_ptr_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *child_type, bool is_const, bool is_volatile, PtrLen ptr_len,
IrInstSrc *sentinel, IrInstSrc *align_value,
uint32_t bit_offset_start, uint32_t host_int_bytes, bool is_allow_zero)
{
if (!is_volatile && ptr_len == PtrLenSingle && sentinel == nullptr && align_value == nullptr &&
bit_offset_start == 0 && host_int_bytes == 0 && is_allow_zero == 0)
{
return ir_build_ptr_type_simple(irb, scope, source_node, child_type, is_const);
}
IrInstSrcPtrType *inst = ir_build_instruction<IrInstSrcPtrType>(irb, scope, source_node);
inst->sentinel = sentinel;
inst->align_value = align_value;
inst->child_type = child_type;
inst->is_const = is_const;
inst->is_volatile = is_volatile;
inst->ptr_len = ptr_len;
inst->bit_offset_start = bit_offset_start;
inst->host_int_bytes = host_int_bytes;
inst->is_allow_zero = is_allow_zero;
if (sentinel) ir_ref_instruction(sentinel, irb->current_basic_block);
if (align_value) ir_ref_instruction(align_value, irb->current_basic_block);
ir_ref_instruction(child_type, irb->current_basic_block);
return &inst->base;
}
static IrInstSrc *ir_build_un_op_lval(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrUnOp op_id,
IrInstSrc *value, LVal lval, ResultLoc *result_loc)
{
IrInstSrcUnOp *instruction = ir_build_instruction<IrInstSrcUnOp>(irb, scope, source_node);
instruction->op_id = op_id;
instruction->value = value;
instruction->lval = lval;
instruction->result_loc = result_loc;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_un_op(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrUnOp op_id,
IrInstSrc *value)
{
return ir_build_un_op_lval(irb, scope, source_node, op_id, value, LValNone, nullptr);
}
static IrInstSrc *ir_build_container_init_list(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
size_t item_count, IrInstSrc **elem_result_loc_list, IrInstSrc *result_loc,
AstNode *init_array_type_source_node)
{
IrInstSrcContainerInitList *container_init_list_instruction =
ir_build_instruction<IrInstSrcContainerInitList>(irb, scope, source_node);
container_init_list_instruction->item_count = item_count;
container_init_list_instruction->elem_result_loc_list = elem_result_loc_list;
container_init_list_instruction->result_loc = result_loc;
container_init_list_instruction->init_array_type_source_node = init_array_type_source_node;
for (size_t i = 0; i < item_count; i += 1) {
ir_ref_instruction(elem_result_loc_list[i], irb->current_basic_block);
}
if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block);
return &container_init_list_instruction->base;
}
static IrInstSrc *ir_build_container_init_fields(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
size_t field_count, IrInstSrcContainerInitFieldsField *fields, IrInstSrc *result_loc)
{
IrInstSrcContainerInitFields *container_init_fields_instruction =
ir_build_instruction<IrInstSrcContainerInitFields>(irb, scope, source_node);
container_init_fields_instruction->field_count = field_count;
container_init_fields_instruction->fields = fields;
container_init_fields_instruction->result_loc = result_loc;
for (size_t i = 0; i < field_count; i += 1) {
ir_ref_instruction(fields[i].result_loc, irb->current_basic_block);
}
if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block);
return &container_init_fields_instruction->base;
}
static IrInstSrc *ir_build_unreachable(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
IrInstSrcUnreachable *inst = ir_build_instruction<IrInstSrcUnreachable>(irb, scope, source_node);
inst->base.is_noreturn = true;
return &inst->base;
}
static IrInstSrcStorePtr *ir_build_store_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *ptr, IrInstSrc *value)
{
IrInstSrcStorePtr *instruction = ir_build_instruction<IrInstSrcStorePtr>(irb, scope, source_node);
instruction->ptr = ptr;
instruction->value = value;
ir_ref_instruction(ptr, irb->current_basic_block);
ir_ref_instruction(value, irb->current_basic_block);
return instruction;
}
static IrInstSrc *ir_build_var_decl_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
ZigVar *var, IrInstSrc *align_value, IrInstSrc *ptr)
{
IrInstSrcDeclVar *inst = ir_build_instruction<IrInstSrcDeclVar>(irb, scope, source_node);
inst->var = var;
inst->align_value = align_value;
inst->ptr = ptr;
if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block);
ir_ref_instruction(ptr, irb->current_basic_block);
return &inst->base;
}
static IrInstSrc *ir_build_export(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *target, IrInstSrc *options)
{
IrInstSrcExport *export_instruction = ir_build_instruction<IrInstSrcExport>(
irb, scope, source_node);
export_instruction->target = target;
export_instruction->options = options;
ir_ref_instruction(target, irb->current_basic_block);
ir_ref_instruction(options, irb->current_basic_block);
return &export_instruction->base;
}
static IrInstSrc *ir_build_extern(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *type, IrInstSrc *options)
{
IrInstSrcExtern *extern_instruction = ir_build_instruction<IrInstSrcExtern>(
irb, scope, source_node);
extern_instruction->type = type;
extern_instruction->options = options;
ir_ref_instruction(type, irb->current_basic_block);
ir_ref_instruction(options, irb->current_basic_block);
return &extern_instruction->base;
}
static IrInstSrc *ir_build_load_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *ptr) {
IrInstSrcLoadPtr *instruction = ir_build_instruction<IrInstSrcLoadPtr>(irb, scope, source_node);
instruction->ptr = ptr;
ir_ref_instruction(ptr, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_typeof_n(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc **values, size_t value_count)
{
assert(value_count >= 2);
IrInstSrcTypeOf *instruction = ir_build_instruction<IrInstSrcTypeOf>(irb, scope, source_node);
instruction->value.list = values;
instruction->value_count = value_count;
for (size_t i = 0; i < value_count; i++)
ir_ref_instruction(values[i], irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_typeof_1(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) {
IrInstSrcTypeOf *instruction = ir_build_instruction<IrInstSrcTypeOf>(irb, scope, source_node);
instruction->value.scalar = value;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_set_cold(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *is_cold) {
IrInstSrcSetCold *instruction = ir_build_instruction<IrInstSrcSetCold>(irb, scope, source_node);
instruction->is_cold = is_cold;
ir_ref_instruction(is_cold, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_set_runtime_safety(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *safety_on)
{
IrInstSrcSetRuntimeSafety *inst = ir_build_instruction<IrInstSrcSetRuntimeSafety>(irb, scope, source_node);
inst->safety_on = safety_on;
ir_ref_instruction(safety_on, irb->current_basic_block);
return &inst->base;
}
static IrInstSrc *ir_build_set_float_mode(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *mode_value)
{
IrInstSrcSetFloatMode *instruction = ir_build_instruction<IrInstSrcSetFloatMode>(irb, scope, source_node);
instruction->mode_value = mode_value;
ir_ref_instruction(mode_value, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_array_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *size,
IrInstSrc *sentinel, IrInstSrc *child_type)
{
IrInstSrcArrayType *instruction = ir_build_instruction<IrInstSrcArrayType>(irb, scope, source_node);
instruction->size = size;
instruction->sentinel = sentinel;
instruction->child_type = child_type;
ir_ref_instruction(size, irb->current_basic_block);
if (sentinel != nullptr) ir_ref_instruction(sentinel, irb->current_basic_block);
ir_ref_instruction(child_type, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_anyframe_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *payload_type)
{
IrInstSrcAnyFrameType *instruction = ir_build_instruction<IrInstSrcAnyFrameType>(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 IrInstSrc *ir_build_slice_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *child_type, bool is_const, bool is_volatile,
IrInstSrc *sentinel, IrInstSrc *align_value, bool is_allow_zero)
{
IrInstSrcSliceType *instruction = ir_build_instruction<IrInstSrcSliceType>(irb, scope, source_node);
instruction->is_const = is_const;
instruction->is_volatile = is_volatile;
instruction->child_type = child_type;
instruction->sentinel = sentinel;
instruction->align_value = align_value;
instruction->is_allow_zero = is_allow_zero;
if (sentinel != nullptr) ir_ref_instruction(sentinel, irb->current_basic_block);
if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block);
ir_ref_instruction(child_type, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_asm_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *asm_template, IrInstSrc **input_list, IrInstSrc **output_types,
ZigVar **output_vars, size_t return_count, bool has_side_effects, bool is_global)
{
IrInstSrcAsm *instruction = ir_build_instruction<IrInstSrcAsm>(irb, scope, source_node);
instruction->asm_template = asm_template;
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;
instruction->is_global = is_global;
assert(source_node->type == NodeTypeAsmExpr);
for (size_t i = 0; i < source_node->data.asm_expr.output_list.length; i += 1) {
IrInstSrc *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) {
IrInstSrc *input_value = input_list[i];
ir_ref_instruction(input_value, irb->current_basic_block);
}
return &instruction->base;
}
static IrInstSrc *ir_build_size_of(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type_value,
bool bit_size)
{
IrInstSrcSizeOf *instruction = ir_build_instruction<IrInstSrcSizeOf>(irb, scope, source_node);
instruction->type_value = type_value;
instruction->bit_size = bit_size;
ir_ref_instruction(type_value, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_test_non_null_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *value)
{
IrInstSrcTestNonNull *instruction = ir_build_instruction<IrInstSrcTestNonNull>(irb, scope, source_node);
instruction->value = value;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_optional_unwrap_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *base_ptr, bool safety_check_on)
{
IrInstSrcOptionalUnwrapPtr *instruction = ir_build_instruction<IrInstSrcOptionalUnwrapPtr>(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 IrInstSrc *ir_build_clz(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type,
IrInstSrc *op)
{
IrInstSrcClz *instruction = ir_build_instruction<IrInstSrcClz>(irb, scope, source_node);
instruction->type = type;
instruction->op = op;
ir_ref_instruction(type, irb->current_basic_block);
ir_ref_instruction(op, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_ctz(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type,
IrInstSrc *op)
{
IrInstSrcCtz *instruction = ir_build_instruction<IrInstSrcCtz>(irb, scope, source_node);
instruction->type = type;
instruction->op = op;
ir_ref_instruction(type, irb->current_basic_block);
ir_ref_instruction(op, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_pop_count(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type,
IrInstSrc *op)
{
IrInstSrcPopCount *instruction = ir_build_instruction<IrInstSrcPopCount>(irb, scope, source_node);
instruction->type = type;
instruction->op = op;
ir_ref_instruction(type, irb->current_basic_block);
ir_ref_instruction(op, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_bswap(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type,
IrInstSrc *op)
{
IrInstSrcBswap *instruction = ir_build_instruction<IrInstSrcBswap>(irb, scope, source_node);
instruction->type = type;
instruction->op = op;
ir_ref_instruction(type, irb->current_basic_block);
ir_ref_instruction(op, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_bit_reverse(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type,
IrInstSrc *op)
{
IrInstSrcBitReverse *instruction = ir_build_instruction<IrInstSrcBitReverse>(irb, scope, source_node);
instruction->type = type;
instruction->op = op;
ir_ref_instruction(type, irb->current_basic_block);
ir_ref_instruction(op, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrcSwitchBr *ir_build_switch_br_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *target_value, IrBasicBlockSrc *else_block, size_t case_count, IrInstSrcSwitchBrCase *cases,
IrInstSrc *is_comptime, IrInstSrc *switch_prongs_void)
{
IrInstSrcSwitchBr *instruction = ir_build_instruction<IrInstSrcSwitchBr>(irb, scope, source_node);
instruction->base.is_noreturn = true;
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);
ir_ref_instruction(is_comptime, irb->current_basic_block);
ir_ref_bb(else_block);
ir_ref_instruction(switch_prongs_void, irb->current_basic_block);
for (size_t i = 0; i < case_count; i += 1) {
ir_ref_instruction(cases[i].value, irb->current_basic_block);
ir_ref_bb(cases[i].block);
}
return instruction;
}
static IrInstSrc *ir_build_switch_target(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *target_value_ptr)
{
IrInstSrcSwitchTarget *instruction = ir_build_instruction<IrInstSrcSwitchTarget>(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 IrInstSrc *ir_build_switch_var(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *target_value_ptr, IrInstSrc **prongs_ptr, size_t prongs_len)
{
IrInstSrcSwitchVar *instruction = ir_build_instruction<IrInstSrcSwitchVar>(irb, scope, source_node);
instruction->target_value_ptr = target_value_ptr;
instruction->prongs_ptr = prongs_ptr;
instruction->prongs_len = prongs_len;
ir_ref_instruction(target_value_ptr, irb->current_basic_block);
for (size_t i = 0; i < prongs_len; i += 1) {
ir_ref_instruction(prongs_ptr[i], irb->current_basic_block);
}
return &instruction->base;
}
// For this instruction the switch_br must be set later.
static IrInstSrcSwitchElseVar *ir_build_switch_else_var(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *target_value_ptr)
{
IrInstSrcSwitchElseVar *instruction = ir_build_instruction<IrInstSrcSwitchElseVar>(irb, scope, source_node);
instruction->target_value_ptr = target_value_ptr;
ir_ref_instruction(target_value_ptr, irb->current_basic_block);
return instruction;
}
static IrInstSrc *ir_build_import(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *name) {
IrInstSrcImport *instruction = ir_build_instruction<IrInstSrcImport>(irb, scope, source_node);
instruction->name = name;
ir_ref_instruction(name, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_ref_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) {
IrInstSrcRef *instruction = ir_build_instruction<IrInstSrcRef>(irb, scope, source_node);
instruction->value = value;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_compile_err(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *msg) {
IrInstSrcCompileErr *instruction = ir_build_instruction<IrInstSrcCompileErr>(irb, scope, source_node);
instruction->msg = msg;
ir_ref_instruction(msg, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_compile_log(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
size_t msg_count, IrInstSrc **msg_list)
{
IrInstSrcCompileLog *instruction = ir_build_instruction<IrInstSrcCompileLog>(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 IrInstSrc *ir_build_err_name(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) {
IrInstSrcErrName *instruction = ir_build_instruction<IrInstSrcErrName>(irb, scope, source_node);
instruction->value = value;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_c_import(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
IrInstSrcCImport *instruction = ir_build_instruction<IrInstSrcCImport>(irb, scope, source_node);
return &instruction->base;
}
static IrInstSrc *ir_build_c_include(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *name) {
IrInstSrcCInclude *instruction = ir_build_instruction<IrInstSrcCInclude>(irb, scope, source_node);
instruction->name = name;
ir_ref_instruction(name, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_c_define(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *name, IrInstSrc *value) {
IrInstSrcCDefine *instruction = ir_build_instruction<IrInstSrcCDefine>(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 IrInstSrc *ir_build_c_undef(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *name) {
IrInstSrcCUndef *instruction = ir_build_instruction<IrInstSrcCUndef>(irb, scope, source_node);
instruction->name = name;
ir_ref_instruction(name, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_embed_file(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *name) {
IrInstSrcEmbedFile *instruction = ir_build_instruction<IrInstSrcEmbedFile>(irb, scope, source_node);
instruction->name = name;
ir_ref_instruction(name, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_cmpxchg_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *type_value, IrInstSrc *ptr, IrInstSrc *cmp_value, IrInstSrc *new_value,
IrInstSrc *success_order_value, IrInstSrc *failure_order_value, bool is_weak, ResultLoc *result_loc)
{
IrInstSrcCmpxchg *instruction = ir_build_instruction<IrInstSrcCmpxchg>(irb, scope, source_node);
instruction->type_value = type_value;
instruction->ptr = ptr;
instruction->cmp_value = cmp_value;
instruction->new_value = new_value;
instruction->success_order_value = success_order_value;
instruction->failure_order_value = failure_order_value;
instruction->is_weak = is_weak;
instruction->result_loc = result_loc;
ir_ref_instruction(type_value, irb->current_basic_block);
ir_ref_instruction(ptr, irb->current_basic_block);
ir_ref_instruction(cmp_value, irb->current_basic_block);
ir_ref_instruction(new_value, irb->current_basic_block);
ir_ref_instruction(success_order_value, irb->current_basic_block);
ir_ref_instruction(failure_order_value, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_fence(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *order) {
IrInstSrcFence *instruction = ir_build_instruction<IrInstSrcFence>(irb, scope, source_node);
instruction->order = order;
ir_ref_instruction(order, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_reduce(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *op, IrInstSrc *value) {
IrInstSrcReduce *instruction = ir_build_instruction<IrInstSrcReduce>(irb, scope, source_node);
instruction->op = op;
instruction->value = value;
ir_ref_instruction(op, irb->current_basic_block);
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_truncate(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *dest_type, IrInstSrc *target)
{
IrInstSrcTruncate *instruction = ir_build_instruction<IrInstSrcTruncate>(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 IrInstSrc *ir_build_int_cast(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *dest_type,
IrInstSrc *target)
{
IrInstSrcIntCast *instruction = ir_build_instruction<IrInstSrcIntCast>(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 IrInstSrc *ir_build_float_cast(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *dest_type,
IrInstSrc *target)
{
IrInstSrcFloatCast *instruction = ir_build_instruction<IrInstSrcFloatCast>(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 IrInstSrc *ir_build_err_set_cast(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *dest_type, IrInstSrc *target)
{
IrInstSrcErrSetCast *instruction = ir_build_instruction<IrInstSrcErrSetCast>(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 IrInstSrc *ir_build_int_to_float(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *dest_type, IrInstSrc *target)
{
IrInstSrcIntToFloat *instruction = ir_build_instruction<IrInstSrcIntToFloat>(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 IrInstSrc *ir_build_float_to_int(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *dest_type, IrInstSrc *target)
{
IrInstSrcFloatToInt *instruction = ir_build_instruction<IrInstSrcFloatToInt>(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 IrInstSrc *ir_build_bool_to_int(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *target) {
IrInstSrcBoolToInt *instruction = ir_build_instruction<IrInstSrcBoolToInt>(irb, scope, source_node);
instruction->target = target;
ir_ref_instruction(target, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_vector_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *len,
IrInstSrc *elem_type)
{
IrInstSrcVectorType *instruction = ir_build_instruction<IrInstSrcVectorType>(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 IrInstSrc *ir_build_shuffle_vector(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *scalar_type, IrInstSrc *a, IrInstSrc *b, IrInstSrc *mask)
{
IrInstSrcShuffleVector *instruction = ir_build_instruction<IrInstSrcShuffleVector>(irb, scope, source_node);
instruction->scalar_type = scalar_type;
instruction->a = a;
instruction->b = b;
instruction->mask = mask;
if (scalar_type != nullptr) ir_ref_instruction(scalar_type, irb->current_basic_block);
ir_ref_instruction(a, irb->current_basic_block);
ir_ref_instruction(b, irb->current_basic_block);
ir_ref_instruction(mask, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_splat_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *len, IrInstSrc *scalar)
{
IrInstSrcSplat *instruction = ir_build_instruction<IrInstSrcSplat>(irb, scope, source_node);
instruction->len = len;
instruction->scalar = scalar;
ir_ref_instruction(len, irb->current_basic_block);
ir_ref_instruction(scalar, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_bool_not(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) {
IrInstSrcBoolNot *instruction = ir_build_instruction<IrInstSrcBoolNot>(irb, scope, source_node);
instruction->value = value;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_memset_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *dest_ptr, IrInstSrc *byte, IrInstSrc *count)
{
IrInstSrcMemset *instruction = ir_build_instruction<IrInstSrcMemset>(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 IrInstSrc *ir_build_memcpy_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *dest_ptr, IrInstSrc *src_ptr, IrInstSrc *count)
{
IrInstSrcMemcpy *instruction = ir_build_instruction<IrInstSrcMemcpy>(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 IrInstSrc *ir_build_slice_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *ptr, IrInstSrc *start, IrInstSrc *end, IrInstSrc *sentinel,
bool safety_check_on, ResultLoc *result_loc)
{
IrInstSrcSlice *instruction = ir_build_instruction<IrInstSrcSlice>(irb, scope, source_node);
instruction->ptr = ptr;
instruction->start = start;
instruction->end = end;
instruction->sentinel = sentinel;
instruction->safety_check_on = safety_check_on;
instruction->result_loc = result_loc;
ir_ref_instruction(ptr, irb->current_basic_block);
ir_ref_instruction(start, irb->current_basic_block);
if (end) ir_ref_instruction(end, irb->current_basic_block);
if (sentinel) ir_ref_instruction(sentinel, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_breakpoint(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
IrInstSrcBreakpoint *instruction = ir_build_instruction<IrInstSrcBreakpoint>(irb, scope, source_node);
return &instruction->base;
}
static IrInstSrc *ir_build_return_address_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
IrInstSrcReturnAddress *instruction = ir_build_instruction<IrInstSrcReturnAddress>(irb, scope, source_node);
return &instruction->base;
}
static IrInstSrc *ir_build_frame_address_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
IrInstSrcFrameAddress *inst = ir_build_instruction<IrInstSrcFrameAddress>(irb, scope, source_node);
return &inst->base;
}
static IrInstSrc *ir_build_handle_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
IrInstSrcFrameHandle *inst = ir_build_instruction<IrInstSrcFrameHandle>(irb, scope, source_node);
return &inst->base;
}
static IrInstSrc *ir_build_frame_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *fn) {
IrInstSrcFrameType *inst = ir_build_instruction<IrInstSrcFrameType>(irb, scope, source_node);
inst->fn = fn;
ir_ref_instruction(fn, irb->current_basic_block);
return &inst->base;
}
static IrInstSrc *ir_build_frame_size_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *fn) {
IrInstSrcFrameSize *inst = ir_build_instruction<IrInstSrcFrameSize>(irb, scope, source_node);
inst->fn = fn;
ir_ref_instruction(fn, irb->current_basic_block);
return &inst->base;
}
static IrInstSrc *ir_build_overflow_op_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrOverflowOp op, IrInstSrc *type_value, IrInstSrc *op1, IrInstSrc *op2, IrInstSrc *result_ptr)
{
IrInstSrcOverflowOp *instruction = ir_build_instruction<IrInstSrcOverflowOp>(irb, scope, source_node);
instruction->op = op;
instruction->type_value = type_value;
instruction->op1 = op1;
instruction->op2 = op2;
instruction->result_ptr = result_ptr;
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 IrInstSrc *ir_build_float_op_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *operand,
BuiltinFnId fn_id)
{
IrInstSrcFloatOp *instruction = ir_build_instruction<IrInstSrcFloatOp>(irb, scope, source_node);
instruction->operand = operand;
instruction->fn_id = fn_id;
ir_ref_instruction(operand, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_mul_add_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *type_value, IrInstSrc *op1, IrInstSrc *op2, IrInstSrc *op3)
{
IrInstSrcMulAdd *instruction = ir_build_instruction<IrInstSrcMulAdd>(irb, scope, source_node);
instruction->type_value = type_value;
instruction->op1 = op1;
instruction->op2 = op2;
instruction->op3 = op3;
ir_ref_instruction(type_value, irb->current_basic_block);
ir_ref_instruction(op1, irb->current_basic_block);
ir_ref_instruction(op2, irb->current_basic_block);
ir_ref_instruction(op3, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_align_of(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type_value) {
IrInstSrcAlignOf *instruction = ir_build_instruction<IrInstSrcAlignOf>(irb, scope, source_node);
instruction->type_value = type_value;
ir_ref_instruction(type_value, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_test_err_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *base_ptr, bool resolve_err_set, bool base_ptr_is_payload)
{
IrInstSrcTestErr *instruction = ir_build_instruction<IrInstSrcTestErr>(irb, scope, source_node);
instruction->base_ptr = base_ptr;
instruction->resolve_err_set = resolve_err_set;
instruction->base_ptr_is_payload = base_ptr_is_payload;
ir_ref_instruction(base_ptr, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_unwrap_err_code_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *err_union_ptr)
{
IrInstSrcUnwrapErrCode *inst = ir_build_instruction<IrInstSrcUnwrapErrCode>(irb, scope, source_node);
inst->err_union_ptr = err_union_ptr;
ir_ref_instruction(err_union_ptr, irb->current_basic_block);
return &inst->base;
}
static IrInstSrc *ir_build_unwrap_err_payload_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *value, bool safety_check_on, bool initializing)
{
IrInstSrcUnwrapErrPayload *inst = ir_build_instruction<IrInstSrcUnwrapErrPayload>(irb, scope, source_node);
inst->value = value;
inst->safety_check_on = safety_check_on;
inst->initializing = initializing;
ir_ref_instruction(value, irb->current_basic_block);
return &inst->base;
}
static IrInstSrc *ir_build_fn_proto(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc **param_types, IrInstSrc *align_value, IrInstSrc *callconv_value,
IrInstSrc *return_type, bool is_var_args)
{
IrInstSrcFnProto *instruction = ir_build_instruction<IrInstSrcFnProto>(irb, scope, source_node);
instruction->param_types = param_types;
instruction->align_value = align_value;
instruction->callconv_value = callconv_value;
instruction->return_type = return_type;
instruction->is_var_args = is_var_args;
assert(source_node->type == NodeTypeFnProto);
size_t param_count = source_node->data.fn_proto.params.length;
if (is_var_args) param_count -= 1;
for (size_t i = 0; i < param_count; i += 1) {
if (param_types[i] != nullptr) ir_ref_instruction(param_types[i], irb->current_basic_block);
}
if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block);
if (callconv_value != nullptr) ir_ref_instruction(callconv_value, irb->current_basic_block);
ir_ref_instruction(return_type, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_test_comptime(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) {
IrInstSrcTestComptime *instruction = ir_build_instruction<IrInstSrcTestComptime>(irb, scope, source_node);
instruction->value = value;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_ptr_cast_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *dest_type, IrInstSrc *ptr, bool safety_check_on)
{
IrInstSrcPtrCast *instruction = ir_build_instruction<IrInstSrcPtrCast>(
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 IrInstSrc *ir_build_implicit_cast(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *operand, ResultLocCast *result_loc_cast)
{
IrInstSrcImplicitCast *instruction = ir_build_instruction<IrInstSrcImplicitCast>(irb, scope, source_node);
instruction->operand = operand;
instruction->result_loc_cast = result_loc_cast;
ir_ref_instruction(operand, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_bit_cast_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *operand, ResultLocBitCast *result_loc_bit_cast)
{
IrInstSrcBitCast *instruction = ir_build_instruction<IrInstSrcBitCast>(irb, scope, source_node);
instruction->operand = operand;
instruction->result_loc_bit_cast = result_loc_bit_cast;
ir_ref_instruction(operand, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_int_to_ptr_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *dest_type, IrInstSrc *target)
{
IrInstSrcIntToPtr *instruction = ir_build_instruction<IrInstSrcIntToPtr>(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 IrInstSrc *ir_build_ptr_to_int_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *target)
{
IrInstSrcPtrToInt *inst = ir_build_instruction<IrInstSrcPtrToInt>(irb, scope, source_node);
inst->target = target;
ir_ref_instruction(target, irb->current_basic_block);
return &inst->base;
}
static IrInstSrc *ir_build_int_to_enum_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *dest_type, IrInstSrc *target)
{
IrInstSrcIntToEnum *instruction = ir_build_instruction<IrInstSrcIntToEnum>(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 IrInstSrc *ir_build_enum_to_int(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *target)
{
IrInstSrcEnumToInt *instruction = ir_build_instruction<IrInstSrcEnumToInt>(
irb, scope, source_node);
instruction->target = target;
ir_ref_instruction(target, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_int_to_err_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *target)
{
IrInstSrcIntToErr *instruction = ir_build_instruction<IrInstSrcIntToErr>(irb, scope, source_node);
instruction->target = target;
ir_ref_instruction(target, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_err_to_int_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *target)
{
IrInstSrcErrToInt *instruction = ir_build_instruction<IrInstSrcErrToInt>(
irb, scope, source_node);
instruction->target = target;
ir_ref_instruction(target, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_check_switch_prongs(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *target_value, IrInstSrcCheckSwitchProngsRange *ranges, size_t range_count,
AstNode* else_prong, bool have_underscore_prong)
{
IrInstSrcCheckSwitchProngs *instruction = heap::c_allocator.create<IrInstSrcCheckSwitchProngs>();
instruction->base.id = have_underscore_prong ?
IrInstSrcIdCheckSwitchProngsUnderYes : IrInstSrcIdCheckSwitchProngsUnderNo;
instruction->base.base.scope = scope;
instruction->base.base.source_node = source_node;
instruction->base.base.debug_id = exec_next_debug_id(irb->exec);
instruction->base.owner_bb = irb->current_basic_block;
ir_instruction_append(irb->current_basic_block, &instruction->base);
instruction->target_value = target_value;
instruction->ranges = ranges;
instruction->range_count = range_count;
instruction->else_prong = 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 IrInstSrc *ir_build_check_statement_is_void(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc* statement_value)
{
IrInstSrcCheckStatementIsVoid *instruction = ir_build_instruction<IrInstSrcCheckStatementIsVoid>(
irb, scope, source_node);
instruction->statement_value = statement_value;
ir_ref_instruction(statement_value, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_type_name(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *type_value)
{
IrInstSrcTypeName *instruction = ir_build_instruction<IrInstSrcTypeName>(irb, scope, source_node);
instruction->type_value = type_value;
ir_ref_instruction(type_value, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_decl_ref(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, Tld *tld, LVal lval) {
IrInstSrcDeclRef *instruction = ir_build_instruction<IrInstSrcDeclRef>(irb, scope, source_node);
instruction->tld = tld;
instruction->lval = lval;
return &instruction->base;
}
static IrInstSrc *ir_build_panic_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *msg) {
IrInstSrcPanic *instruction = ir_build_instruction<IrInstSrcPanic>(irb, scope, source_node);
instruction->base.is_noreturn = true;
instruction->msg = msg;
ir_ref_instruction(msg, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_tag_name_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *target) {
IrInstSrcTagName *instruction = ir_build_instruction<IrInstSrcTagName>(irb, scope, source_node);
instruction->target = target;
ir_ref_instruction(target, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_field_parent_ptr_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *type_value, IrInstSrc *field_name, IrInstSrc *field_ptr)
{
IrInstSrcFieldParentPtr *inst = ir_build_instruction<IrInstSrcFieldParentPtr>(
irb, scope, source_node);
inst->type_value = type_value;
inst->field_name = field_name;
inst->field_ptr = field_ptr;
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 &inst->base;
}
static IrInstSrc *ir_build_byte_offset_of(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *type_value, IrInstSrc *field_name)
{
IrInstSrcByteOffsetOf *instruction = ir_build_instruction<IrInstSrcByteOffsetOf>(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 IrInstSrc *ir_build_bit_offset_of(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *type_value, IrInstSrc *field_name)
{
IrInstSrcBitOffsetOf *instruction = ir_build_instruction<IrInstSrcBitOffsetOf>(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 IrInstSrc *ir_build_type_info(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type_value) {
IrInstSrcTypeInfo *instruction = ir_build_instruction<IrInstSrcTypeInfo>(irb, scope, source_node);
instruction->type_value = type_value;
ir_ref_instruction(type_value, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type_info) {
IrInstSrcType *instruction = ir_build_instruction<IrInstSrcType>(irb, scope, source_node);
instruction->type_info = type_info;
ir_ref_instruction(type_info, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_set_eval_branch_quota(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *new_quota)
{
IrInstSrcSetEvalBranchQuota *instruction = ir_build_instruction<IrInstSrcSetEvalBranchQuota>(irb, scope, source_node);
instruction->new_quota = new_quota;
ir_ref_instruction(new_quota, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_align_cast_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *align_bytes, IrInstSrc *target)
{
IrInstSrcAlignCast *instruction = ir_build_instruction<IrInstSrcAlignCast>(irb, scope, source_node);
instruction->align_bytes = align_bytes;
instruction->target = target;
ir_ref_instruction(align_bytes, irb->current_basic_block);
ir_ref_instruction(target, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_resolve_result(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
ResultLoc *result_loc, IrInstSrc *ty)
{
IrInstSrcResolveResult *instruction = ir_build_instruction<IrInstSrcResolveResult>(irb, scope, source_node);
instruction->result_loc = result_loc;
instruction->ty = ty;
if (ty != nullptr) ir_ref_instruction(ty, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_reset_result(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
ResultLoc *result_loc)
{
IrInstSrcResetResult *instruction = ir_build_instruction<IrInstSrcResetResult>(irb, scope, source_node);
instruction->result_loc = result_loc;
instruction->base.is_gen = true;
return &instruction->base;
}
static IrInstSrc *ir_build_set_align_stack(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *align_bytes)
{
IrInstSrcSetAlignStack *instruction = ir_build_instruction<IrInstSrcSetAlignStack>(irb, scope, source_node);
instruction->align_bytes = align_bytes;
ir_ref_instruction(align_bytes, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_arg_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *fn_type, IrInstSrc *arg_index, bool allow_var)
{
IrInstSrcArgType *instruction = heap::c_allocator.create<IrInstSrcArgType>();
instruction->base.id = allow_var ?
IrInstSrcIdArgTypeAllowVarTrue : IrInstSrcIdArgTypeAllowVarFalse;
instruction->base.base.scope = scope;
instruction->base.base.source_node = source_node;
instruction->base.base.debug_id = exec_next_debug_id(irb->exec);
instruction->base.owner_bb = irb->current_basic_block;
ir_instruction_append(irb->current_basic_block, &instruction->base);
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 IrInstSrc *ir_build_error_return_trace_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstErrorReturnTraceOptional optional)
{
IrInstSrcErrorReturnTrace *inst = ir_build_instruction<IrInstSrcErrorReturnTrace>(irb, scope, source_node);
inst->optional = optional;
return &inst->base;
}
static IrInstSrc *ir_build_error_union(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *err_set, IrInstSrc *payload)
{
IrInstSrcErrorUnion *instruction = ir_build_instruction<IrInstSrcErrorUnion>(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 IrInstSrc *ir_build_atomic_rmw_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *operand_type, IrInstSrc *ptr, IrInstSrc *op, IrInstSrc *operand,
IrInstSrc *ordering)
{
IrInstSrcAtomicRmw *instruction = ir_build_instruction<IrInstSrcAtomicRmw>(irb, scope, source_node);
instruction->operand_type = operand_type;
instruction->ptr = ptr;
instruction->op = op;
instruction->operand = operand;
instruction->ordering = ordering;
ir_ref_instruction(operand_type, irb->current_basic_block);
ir_ref_instruction(ptr, irb->current_basic_block);
ir_ref_instruction(op, irb->current_basic_block);
ir_ref_instruction(operand, irb->current_basic_block);
ir_ref_instruction(ordering, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_atomic_load_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *operand_type, IrInstSrc *ptr, IrInstSrc *ordering)
{
IrInstSrcAtomicLoad *instruction = ir_build_instruction<IrInstSrcAtomicLoad>(irb, scope, source_node);
instruction->operand_type = operand_type;
instruction->ptr = ptr;
instruction->ordering = ordering;
ir_ref_instruction(operand_type, irb->current_basic_block);
ir_ref_instruction(ptr, irb->current_basic_block);
ir_ref_instruction(ordering, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_atomic_store_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *operand_type, IrInstSrc *ptr, IrInstSrc *value, IrInstSrc *ordering)
{
IrInstSrcAtomicStore *instruction = ir_build_instruction<IrInstSrcAtomicStore>(irb, scope, source_node);
instruction->operand_type = operand_type;
instruction->ptr = ptr;
instruction->value = value;
instruction->ordering = ordering;
ir_ref_instruction(operand_type, irb->current_basic_block);
ir_ref_instruction(ptr, irb->current_basic_block);
ir_ref_instruction(value, irb->current_basic_block);
ir_ref_instruction(ordering, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_save_err_ret_addr_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
IrInstSrcSaveErrRetAddr *inst = ir_build_instruction<IrInstSrcSaveErrRetAddr>(irb, scope, source_node);
return &inst->base;
}
static IrInstSrc *ir_build_add_implicit_return_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *value, ResultLocReturn *result_loc_ret)
{
IrInstSrcAddImplicitReturnType *inst = ir_build_instruction<IrInstSrcAddImplicitReturnType>(irb, scope, source_node);
inst->value = value;
inst->result_loc_ret = result_loc_ret;
ir_ref_instruction(value, irb->current_basic_block);
return &inst->base;
}
static IrInstSrc *ir_build_has_decl(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *container, IrInstSrc *name)
{
IrInstSrcHasDecl *instruction = ir_build_instruction<IrInstSrcHasDecl>(irb, scope, source_node);
instruction->container = container;
instruction->name = name;
ir_ref_instruction(container, irb->current_basic_block);
ir_ref_instruction(name, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_undeclared_identifier(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, Buf *name) {
IrInstSrcUndeclaredIdent *instruction = ir_build_instruction<IrInstSrcUndeclaredIdent>(irb, scope, source_node);
instruction->name = name;
return &instruction->base;
}
static IrInstSrc *ir_build_check_runtime_scope(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *scope_is_comptime, IrInstSrc *is_comptime) {
IrInstSrcCheckRuntimeScope *instruction = ir_build_instruction<IrInstSrcCheckRuntimeScope>(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 IrInstSrc *ir_build_union_init_named_field(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *union_type, IrInstSrc *field_name, IrInstSrc *field_result_loc, IrInstSrc *result_loc)
{
IrInstSrcUnionInitNamedField *instruction = ir_build_instruction<IrInstSrcUnionInitNamedField>(irb, scope, source_node);
instruction->union_type = union_type;
instruction->field_name = field_name;
instruction->field_result_loc = field_result_loc;
instruction->result_loc = result_loc;
ir_ref_instruction(union_type, irb->current_basic_block);
ir_ref_instruction(field_name, irb->current_basic_block);
ir_ref_instruction(field_result_loc, irb->current_basic_block);
if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_alloca_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *align, const char *name_hint, IrInstSrc *is_comptime)
{
IrInstSrcAlloca *instruction = ir_build_instruction<IrInstSrcAlloca>(irb, scope, source_node);
instruction->base.is_gen = true;
instruction->align = align;
instruction->name_hint = name_hint;
instruction->is_comptime = is_comptime;
if (align != nullptr) ir_ref_instruction(align, irb->current_basic_block);
if (is_comptime != nullptr) ir_ref_instruction(is_comptime, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_end_expr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *value, ResultLoc *result_loc)
{
IrInstSrcEndExpr *instruction = ir_build_instruction<IrInstSrcEndExpr>(irb, scope, source_node);
instruction->base.is_gen = true;
instruction->value = value;
instruction->result_loc = result_loc;
ir_ref_instruction(value, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrcSuspendBegin *ir_build_suspend_begin_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
return ir_build_instruction<IrInstSrcSuspendBegin>(irb, scope, source_node);
}
static IrInstSrc *ir_build_suspend_finish_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrcSuspendBegin *begin)
{
IrInstSrcSuspendFinish *inst = ir_build_instruction<IrInstSrcSuspendFinish>(irb, scope, source_node);
inst->begin = begin;
ir_ref_instruction(&begin->base, irb->current_basic_block);
return &inst->base;
}
static IrInstSrc *ir_build_await_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *frame, ResultLoc *result_loc, bool is_nosuspend)
{
IrInstSrcAwait *instruction = ir_build_instruction<IrInstSrcAwait>(irb, scope, source_node);
instruction->frame = frame;
instruction->result_loc = result_loc;
instruction->is_nosuspend = is_nosuspend;
ir_ref_instruction(frame, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_resume_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *frame) {
IrInstSrcResume *instruction = ir_build_instruction<IrInstSrcResume>(irb, scope, source_node);
instruction->frame = frame;
ir_ref_instruction(frame, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrcSpillBegin *ir_build_spill_begin_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *operand, SpillId spill_id)
{
IrInstSrcSpillBegin *instruction = ir_build_instruction<IrInstSrcSpillBegin>(irb, scope, source_node);
instruction->operand = operand;
instruction->spill_id = spill_id;
ir_ref_instruction(operand, irb->current_basic_block);
return instruction;
}
static IrInstSrc *ir_build_spill_end_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrcSpillBegin *begin)
{
IrInstSrcSpillEnd *instruction = ir_build_instruction<IrInstSrcSpillEnd>(irb, scope, source_node);
instruction->begin = begin;
ir_ref_instruction(&begin->base, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_wasm_memory_size_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *index) {
IrInstSrcWasmMemorySize *instruction = ir_build_instruction<IrInstSrcWasmMemorySize>(irb, scope, source_node);
instruction->index = index;
ir_ref_instruction(index, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_wasm_memory_grow_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *index, IrInstSrc *delta) {
IrInstSrcWasmMemoryGrow *instruction = ir_build_instruction<IrInstSrcWasmMemoryGrow>(irb, scope, source_node);
instruction->index = index;
instruction->delta = delta;
ir_ref_instruction(index, irb->current_basic_block);
ir_ref_instruction(delta, irb->current_basic_block);
return &instruction->base;
}
static IrInstSrc *ir_build_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
IrInstSrcSrc *instruction = ir_build_instruction<IrInstSrcSrc>(irb, scope, source_node);
return &instruction->base;
}
static void ir_count_defers(IrBuilderSrc *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 ScopeIdNoSuspend:
case ScopeIdRuntime:
case ScopeIdTypeOf:
case ScopeIdExpr:
scope = scope->parent;
continue;
case ScopeIdDeferExpr:
case ScopeIdCImport:
zig_unreachable();
}
}
}
static IrInstSrc *ir_mark_gen(IrInstSrc *instruction) {
instruction->is_gen = true;
return instruction;
}
static bool ir_gen_defers_for_block(IrBuilderSrc *irb, Scope *inner_scope, Scope *outer_scope, bool *is_noreturn, IrInstSrc *err_value) {
Scope *scope = inner_scope;
if (is_noreturn != nullptr) *is_noreturn = false;
while (scope != outer_scope) {
if (!scope)
return true;
switch (scope->id) {
case ScopeIdDefer: {
AstNode *defer_node = scope->source_node;
assert(defer_node->type == NodeTypeDefer);
ReturnKind defer_kind = defer_node->data.defer.kind;
AstNode *defer_expr_node = defer_node->data.defer.expr;
AstNode *defer_var_node = defer_node->data.defer.err_payload;
if (defer_kind == ReturnKindError && err_value == nullptr) {
// This is an `errdefer` but we're generating code for a
// `return` that doesn't return an error, skip it
scope = scope->parent;
continue;
}
Scope *defer_expr_scope = defer_node->data.defer.expr_scope;
if (defer_var_node != nullptr) {
assert(defer_kind == ReturnKindError);
assert(defer_var_node->type == NodeTypeIdentifier);
Buf *var_name = node_identifier_buf(defer_var_node);
if (defer_expr_node->type == NodeTypeUnreachable) {
add_node_error(irb->codegen, defer_var_node,
buf_sprintf("unused variable: '%s'", buf_ptr(var_name)));
return false;
}
IrInstSrc *is_comptime;
if (ir_should_inline(irb->exec, defer_expr_scope)) {
is_comptime = ir_build_const_bool(irb, defer_expr_scope,
defer_expr_node, true);
} else {
is_comptime = ir_build_test_comptime(irb, defer_expr_scope,
defer_expr_node, err_value);
}
ZigVar *err_var = ir_create_var(irb, defer_var_node, defer_expr_scope,
var_name, true, true, false, is_comptime);
build_decl_var_and_init(irb, defer_expr_scope, defer_var_node, err_var, err_value,
buf_ptr(var_name), is_comptime);
defer_expr_scope = err_var->child_scope;
}
IrInstSrc *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope);
if (defer_expr_value == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
if (defer_expr_value->is_noreturn) {
if (is_noreturn != nullptr) *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 true;
case ScopeIdBlock:
case ScopeIdVarDecl:
case ScopeIdLoop:
case ScopeIdSuspend:
case ScopeIdCompTime:
case ScopeIdNoSuspend:
case ScopeIdRuntime:
case ScopeIdTypeOf:
case ScopeIdExpr:
scope = scope->parent;
continue;
case ScopeIdDeferExpr:
case ScopeIdCImport:
zig_unreachable();
}
}
return true;
}
static void ir_set_cursor_at_end(IrBuilderSrc *irb, IrBasicBlockSrc *basic_block) {
assert(basic_block);
irb->current_basic_block = basic_block;
}
static void ir_set_cursor_at_end_and_append_block(IrBuilderSrc *irb, IrBasicBlockSrc *basic_block) {
basic_block->index = irb->exec->basic_block_list.length;
irb->exec->basic_block_list.append(basic_block);
ir_set_cursor_at_end(irb, basic_block);
}
static ScopeSuspend *get_scope_suspend(Scope *scope) {
while (scope) {
if (scope->id == ScopeIdSuspend)
return (ScopeSuspend *)scope;
if (scope->id == ScopeIdFnDef)
return nullptr;
scope = scope->parent;
}
return nullptr;
}
static ScopeDeferExpr *get_scope_defer_expr(Scope *scope) {
while (scope) {
if (scope->id == ScopeIdDeferExpr)
return (ScopeDeferExpr *)scope;
if (scope->id == ScopeIdFnDef)
return nullptr;
scope = scope->parent;
}
return nullptr;
}
static IrInstSrc *ir_gen_return(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) {
assert(node->type == NodeTypeReturnExpr);
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_inst_src;
}
Scope *outer_scope = irb->exec->begin_scope;
AstNode *expr_node = node->data.return_expr.expr;
switch (node->data.return_expr.kind) {
case ReturnKindUnconditional:
{
ResultLocReturn *result_loc_ret = heap::c_allocator.create<ResultLocReturn>();
result_loc_ret->base.id = ResultLocIdReturn;
ir_build_reset_result(irb, scope, node, &result_loc_ret->base);
IrInstSrc *return_value;
if (expr_node) {
// Temporarily set this so that if we return a type it gets the name of the function
ZigFn *prev_name_fn = irb->exec->name_fn;
irb->exec->name_fn = exec_fn_entry(irb->exec);
return_value = ir_gen_node_extra(irb, expr_node, scope, LValNone, &result_loc_ret->base);
irb->exec->name_fn = prev_name_fn;
if (return_value == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
} else {
return_value = ir_build_const_void(irb, scope, node);
ir_build_end_expr(irb, scope, node, return_value, &result_loc_ret->base);
}
ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, return_value, result_loc_ret));
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) {
// only generate unconditional defers
if (!ir_gen_defers_for_block(irb, scope, outer_scope, nullptr, nullptr))
return irb->codegen->invalid_inst_src;
IrInstSrc *result = ir_build_return_src(irb, scope, node, nullptr);
result_loc_ret->base.source_instruction = result;
return result;
}
bool should_inline = ir_should_inline(irb->exec, scope);
IrBasicBlockSrc *err_block = ir_create_basic_block(irb, scope, "ErrRetErr");
IrBasicBlockSrc *ok_block = ir_create_basic_block(irb, scope, "ErrRetOk");
IrInstSrc *is_err = ir_build_test_err_src(irb, scope, node, return_value, false, true);
IrInstSrc *is_comptime;
if (should_inline) {
is_comptime = ir_build_const_bool(irb, scope, node, should_inline);
} 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));
IrBasicBlockSrc *ret_stmt_block = ir_create_basic_block(irb, scope, "RetStmt");
ir_set_cursor_at_end_and_append_block(irb, err_block);
if (!ir_gen_defers_for_block(irb, scope, outer_scope, nullptr, return_value))
return irb->codegen->invalid_inst_src;
if (irb->codegen->have_err_ret_tracing && !should_inline) {
ir_build_save_err_ret_addr_src(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 (!ir_gen_defers_for_block(irb, scope, outer_scope, nullptr, nullptr))
return irb->codegen->invalid_inst_src;
ir_build_br(irb, scope, node, ret_stmt_block, is_comptime);
ir_set_cursor_at_end_and_append_block(irb, ret_stmt_block);
IrInstSrc *result = ir_build_return_src(irb, scope, node, nullptr);
result_loc_ret->base.source_instruction = result;
return result;
}
case ReturnKindError:
{
assert(expr_node);
IrInstSrc *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr);
if (err_union_ptr == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
IrInstSrc *is_err_val = ir_build_test_err_src(irb, scope, node, err_union_ptr, true, false);
IrBasicBlockSrc *return_block = ir_create_basic_block(irb, scope, "ErrRetReturn");
IrBasicBlockSrc *continue_block = ir_create_basic_block(irb, scope, "ErrRetContinue");
IrInstSrc *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);
IrInstSrc *err_val_ptr = ir_build_unwrap_err_code_src(irb, scope, node, err_union_ptr);
IrInstSrc *err_val = ir_build_load_ptr(irb, scope, node, err_val_ptr);
ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, err_val, nullptr));
IrInstSrcSpillBegin *spill_begin = ir_build_spill_begin_src(irb, scope, node, err_val,
SpillIdRetErrCode);
ResultLocReturn *result_loc_ret = heap::c_allocator.create<ResultLocReturn>();
result_loc_ret->base.id = ResultLocIdReturn;
ir_build_reset_result(irb, scope, node, &result_loc_ret->base);
ir_build_end_expr(irb, scope, node, err_val, &result_loc_ret->base);
bool is_noreturn = false;
if (!ir_gen_defers_for_block(irb, scope, outer_scope, &is_noreturn, err_val)) {
return irb->codegen->invalid_inst_src;
}
if (!is_noreturn) {
if (irb->codegen->have_err_ret_tracing && !should_inline) {
ir_build_save_err_ret_addr_src(irb, scope, node);
}
err_val = ir_build_spill_end_src(irb, scope, node, spill_begin);
IrInstSrc *ret_inst = ir_build_return_src(irb, scope, node, err_val);
result_loc_ret->base.source_instruction = ret_inst;
}
ir_set_cursor_at_end_and_append_block(irb, continue_block);
IrInstSrc *unwrapped_ptr = ir_build_unwrap_err_payload_src(irb, scope, node, err_union_ptr, false, false);
if (lval == LValPtr)
return unwrapped_ptr;
else
return ir_expr_wrap(irb, scope, ir_build_load_ptr(irb, scope, node, unwrapped_ptr), result_loc);
}
}
zig_unreachable();
}
ZigVar *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_scope,
Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstSrc *is_comptime,
bool skip_name_check)
{
ZigVar *variable_entry = heap::c_allocator.create<ZigVar>();
variable_entry->parent_scope = parent_scope;
variable_entry->shadowable = is_shadowable;
variable_entry->is_comptime = is_comptime;
variable_entry->src_arg_index = SIZE_MAX;
variable_entry->const_value = codegen->pass1_arena->create<ZigValue>();
if (is_comptime != nullptr) {
is_comptime->base.ref_count += 1;
}
if (name) {
variable_entry->name = strdup(buf_ptr(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) {
bool want_err_msg = true;
if (tld->id == TldIdVar) {
ZigVar *var = reinterpret_cast<TldVar *>(tld)->var;
if (var != nullptr && var->var_type != nullptr && type_is_invalid(var->var_type)) {
want_err_msg = false;
}
}
if (want_err_msg) {
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
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(IrBuilderSrc *irb, AstNode *node, Scope *scope, Buf *name,
bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstSrc *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);
assert(var->child_scope);
return var;
}
static ResultLocPeer *create_peer_result(ResultLocPeerParent *peer_parent) {
ResultLocPeer *result = heap::c_allocator.create<ResultLocPeer>();
result->base.id = ResultLocIdPeer;
result->base.source_instruction = peer_parent->base.source_instruction;
result->parent = peer_parent;
result->base.allow_write_through_const = peer_parent->parent->allow_write_through_const;
return result;
}
static bool is_duplicate_label(CodeGen *g, Scope *scope, AstNode *node, Buf *name) {
if (name == nullptr) return false;
for (;;) {
if (scope == nullptr || scope->id == ScopeIdFnDef) {
break;
} else if (scope->id == ScopeIdBlock || scope->id == ScopeIdLoop) {
Buf *this_block_name = scope->id == ScopeIdBlock ? ((ScopeBlock *)scope)->name : ((ScopeLoop *)scope)->name;
if (this_block_name != nullptr && buf_eql_buf(name, this_block_name)) {
ErrorMsg *msg = add_node_error(g, node, buf_sprintf("redeclaration of label '%s'", buf_ptr(name)));
add_error_note(g, msg, scope->source_node, buf_sprintf("previous declaration is here"));
return true;
}
}
scope = scope->parent;
}
return false;
}
static IrInstSrc *ir_gen_block(IrBuilderSrc *irb, Scope *parent_scope, AstNode *block_node, LVal lval,
ResultLoc *result_loc)
{
assert(block_node->type == NodeTypeBlock);
ZigList<IrInstSrc *> incoming_values = {0};
ZigList<IrBasicBlockSrc *> incoming_blocks = {0};
if (is_duplicate_label(irb->codegen, parent_scope, block_node, block_node->data.block.name))
return irb->codegen->invalid_inst_src;
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) {
if (scope_block->name != nullptr) {
add_node_error(irb->codegen, block_node, buf_sprintf("unused block label"));
}
// {}
return ir_lval_wrap(irb, parent_scope, ir_build_const_void(irb, child_scope, block_node), lval, result_loc);
}
if (block_node->data.block.name != nullptr) {
scope_block->lval = lval;
scope_block->incoming_blocks = &incoming_blocks;
scope_block->incoming_values = &incoming_values;
scope_block->end_block = ir_create_basic_block(irb, parent_scope, "BlockEnd");
scope_block->is_comptime = ir_build_const_bool(irb, parent_scope, block_node,
ir_should_inline(irb->exec, parent_scope));
scope_block->peer_parent = heap::c_allocator.create<ResultLocPeerParent>();
scope_block->peer_parent->base.id = ResultLocIdPeerParent;
scope_block->peer_parent->base.source_instruction = scope_block->is_comptime;
scope_block->peer_parent->base.allow_write_through_const = result_loc->allow_write_through_const;
scope_block->peer_parent->end_bb = scope_block->end_block;
scope_block->peer_parent->is_comptime = scope_block->is_comptime;
scope_block->peer_parent->parent = result_loc;
ir_build_reset_result(irb, parent_scope, block_node, &scope_block->peer_parent->base);
}
bool is_continuation_unreachable = false;
bool found_invalid_inst = false;
IrInstSrc *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);
IrInstSrc *statement_value = ir_gen_node(irb, statement_node, child_scope);
if (statement_value == irb->codegen->invalid_inst_src) {
// keep generating all the elements of the block in case of error,
// we want to collect other compile errors
found_invalid_inst = true;
continue;
}
is_continuation_unreachable = instr_is_unreachable(statement_value);
if (is_continuation_unreachable) {
// keep the last noreturn statement value around in case we need to return it
noreturn_return_value = statement_value;
}
// This logic must be kept in sync with
// [STMT_EXPR_TEST_THING] <--- (search this token)
if (statement_node->type == NodeTypeDefer) {
// defer starts a new scope
child_scope = statement_node->data.defer.child_scope;
assert(child_scope);
} else if (statement_value->id == IrInstSrcIdDeclVar) {
// variable declarations start a new scope
IrInstSrcDeclVar *decl_var_instruction = (IrInstSrcDeclVar *)statement_value;
child_scope = decl_var_instruction->var->child_scope;
} else if (!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 (scope_block->name != nullptr && scope_block->name_used == false) {
add_node_error(irb->codegen, block_node, buf_sprintf("unused block label"));
}
if (found_invalid_inst)
return irb->codegen->invalid_inst_src;
if (is_continuation_unreachable) {
assert(noreturn_return_value != nullptr);
if (block_node->data.block.name == nullptr || incoming_blocks.length == 0) {
return noreturn_return_value;
}
if (scope_block->peer_parent != nullptr && scope_block->peer_parent->peers.length != 0) {
scope_block->peer_parent->peers.last()->next_bb = scope_block->end_block;
}
ir_set_cursor_at_end_and_append_block(irb, scope_block->end_block);
IrInstSrc *phi = ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length,
incoming_blocks.items, incoming_values.items, scope_block->peer_parent);
return ir_expr_wrap(irb, parent_scope, phi, result_loc);
} else {
incoming_blocks.append(irb->current_basic_block);
IrInstSrc *else_expr_result = ir_mark_gen(ir_build_const_void(irb, parent_scope, block_node));
if (scope_block->peer_parent != nullptr) {
ResultLocPeer *peer_result = create_peer_result(scope_block->peer_parent);
scope_block->peer_parent->peers.append(peer_result);
ir_build_end_expr(irb, parent_scope, block_node, else_expr_result, &peer_result->base);
if (scope_block->peer_parent->peers.length != 0) {
scope_block->peer_parent->peers.last()->next_bb = scope_block->end_block;
}
}
incoming_values.append(else_expr_result);
}
bool is_return_from_fn = block_node == irb->main_block_node;
if (!is_return_from_fn) {
if (!ir_gen_defers_for_block(irb, child_scope, outer_block_scope, nullptr, nullptr))
return irb->codegen->invalid_inst_src;
}
IrInstSrc *result;
if (block_node->data.block.name != nullptr) {
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);
IrInstSrc *phi = ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length,
incoming_blocks.items, incoming_values.items, scope_block->peer_parent);
result = ir_expr_wrap(irb, parent_scope, phi, result_loc);
} else {
IrInstSrc *void_inst = ir_mark_gen(ir_build_const_void(irb, child_scope, block_node));
result = ir_lval_wrap(irb, parent_scope, void_inst, lval, result_loc);
}
if (!is_return_from_fn)
return result;
// no need for save_err_ret_addr because this cannot return error
// only generate unconditional defers
ir_mark_gen(ir_build_add_implicit_return_type(irb, child_scope, block_node, result, nullptr));
ResultLocReturn *result_loc_ret = heap::c_allocator.create<ResultLocReturn>();
result_loc_ret->base.id = ResultLocIdReturn;
ir_build_reset_result(irb, parent_scope, block_node, &result_loc_ret->base);
ir_mark_gen(ir_build_end_expr(irb, parent_scope, block_node, result, &result_loc_ret->base));
if (!ir_gen_defers_for_block(irb, child_scope, outer_block_scope, nullptr, nullptr))
return irb->codegen->invalid_inst_src;
return ir_mark_gen(ir_build_return_src(irb, child_scope, result->base.source_node, result));
}
static IrInstSrc *ir_gen_bin_op_id(IrBuilderSrc *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);
}
IrInstSrc *op1 = ir_gen_node(irb, node->data.bin_op_expr.op1, inner_scope);
IrInstSrc *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, inner_scope);
if (op1 == irb->codegen->invalid_inst_src || op2 == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
return ir_build_bin_op(irb, scope, node, op_id, op1, op2, true);
}
static IrInstSrc *ir_gen_merge_err_sets(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
IrInstSrc *op1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope);
IrInstSrc *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope);
if (op1 == irb->codegen->invalid_inst_src || op2 == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
// TODO only pass type_name when the || operator is the top level AST node in the var decl expr
Buf bare_name = BUF_INIT;
Buf *type_name = get_anon_type_name(irb->codegen, irb->exec, "error", scope, node, &bare_name);
return ir_build_merge_err_sets(irb, scope, node, op1, op2, type_name);
}
static IrInstSrc *ir_gen_assign(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
IrInstSrc *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValAssign, nullptr);
if (lvalue == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
ResultLocInstruction *result_loc_inst = heap::c_allocator.create<ResultLocInstruction>();
result_loc_inst->base.id = ResultLocIdInstruction;
result_loc_inst->base.source_instruction = lvalue;
ir_ref_instruction(lvalue, irb->current_basic_block);
ir_build_reset_result(irb, scope, node, &result_loc_inst->base);
IrInstSrc *rvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op2, scope, LValNone,
&result_loc_inst->base);
if (rvalue == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
return ir_build_const_void(irb, scope, node);
}
static IrInstSrc *ir_gen_assign_op(IrBuilderSrc *irb, Scope *scope, AstNode *node, IrBinOp op_id) {
IrInstSrc *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValAssign, nullptr);
if (lvalue == irb->codegen->invalid_inst_src)
return lvalue;
IrInstSrc *op1 = ir_build_load_ptr(irb, scope, node->data.bin_op_expr.op1, lvalue);
IrInstSrc *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope);
if (op2 == irb->codegen->invalid_inst_src)
return op2;
IrInstSrc *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 IrInstSrc *ir_gen_bool_or(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeBinOpExpr);
IrInstSrc *val1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope);
if (val1 == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
IrBasicBlockSrc *post_val1_block = irb->current_basic_block;
IrInstSrc *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
IrBasicBlockSrc *false_block = ir_create_basic_block(irb, scope, "BoolOrFalse");
// block for when val1 == true (don't even evaluate the second part)
IrBasicBlockSrc *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);
IrInstSrc *val2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope);
if (val2 == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
IrBasicBlockSrc *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);
IrInstSrc **incoming_values = heap::c_allocator.allocate<IrInstSrc *>(2);
incoming_values[0] = val1;
incoming_values[1] = val2;
IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate<IrBasicBlockSrc *>(2);
incoming_blocks[0] = post_val1_block;
incoming_blocks[1] = post_val2_block;
return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, nullptr);
}
static IrInstSrc *ir_gen_bool_and(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeBinOpExpr);
IrInstSrc *val1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope);
if (val1 == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
IrBasicBlockSrc *post_val1_block = irb->current_basic_block;
IrInstSrc *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
IrBasicBlockSrc *true_block = ir_create_basic_block(irb, scope, "BoolAndTrue");
// block for when val1 == false (don't even evaluate the second part)
IrBasicBlockSrc *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);
IrInstSrc *val2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope);
if (val2 == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
IrBasicBlockSrc *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);
IrInstSrc **incoming_values = heap::c_allocator.allocate<IrInstSrc *>(2);
incoming_values[0] = val1;
incoming_values[1] = val2;
IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate<IrBasicBlockSrc *>(2);
incoming_blocks[0] = post_val1_block;
incoming_blocks[1] = post_val2_block;
return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, nullptr);
}
static ResultLocPeerParent *ir_build_result_peers(IrBuilderSrc *irb, IrInstSrc *cond_br_inst,
IrBasicBlockSrc *end_block, ResultLoc *parent, IrInstSrc *is_comptime)
{
ResultLocPeerParent *peer_parent = heap::c_allocator.create<ResultLocPeerParent>();
peer_parent->base.id = ResultLocIdPeerParent;
peer_parent->base.source_instruction = cond_br_inst;
peer_parent->base.allow_write_through_const = parent->allow_write_through_const;
peer_parent->end_bb = end_block;
peer_parent->is_comptime = is_comptime;
peer_parent->parent = parent;
IrInstSrc *popped_inst = irb->current_basic_block->instruction_list.pop();
ir_assert(popped_inst == cond_br_inst, &cond_br_inst->base);
ir_build_reset_result(irb, cond_br_inst->base.scope, cond_br_inst->base.source_node, &peer_parent->base);
irb->current_basic_block->instruction_list.append(popped_inst);
return peer_parent;
}
static ResultLocPeerParent *ir_build_binary_result_peers(IrBuilderSrc *irb, IrInstSrc *cond_br_inst,
IrBasicBlockSrc *else_block, IrBasicBlockSrc *end_block, ResultLoc *parent, IrInstSrc *is_comptime)
{
ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, parent, is_comptime);
peer_parent->peers.append(create_peer_result(peer_parent));
peer_parent->peers.last()->next_bb = else_block;
peer_parent->peers.append(create_peer_result(peer_parent));
peer_parent->peers.last()->next_bb = end_block;
return peer_parent;
}
static IrInstSrc *ir_gen_orelse(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node, LVal lval,
ResultLoc *result_loc)
{
assert(node->type == NodeTypeBinOpExpr);
AstNode *op1_node = node->data.bin_op_expr.op1;
AstNode *op2_node = node->data.bin_op_expr.op2;
IrInstSrc *maybe_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LValPtr, nullptr);
if (maybe_ptr == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
IrInstSrc *maybe_val = ir_build_load_ptr(irb, parent_scope, node, maybe_ptr);
IrInstSrc *is_non_null = ir_build_test_non_null_src(irb, parent_scope, node, maybe_val);
IrInstSrc *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);
}
IrBasicBlockSrc *ok_block = ir_create_basic_block(irb, parent_scope, "OptionalNonNull");
IrBasicBlockSrc *null_block = ir_create_basic_block(irb, parent_scope, "OptionalNull");
IrBasicBlockSrc *end_block = ir_create_basic_block(irb, parent_scope, "OptionalEnd");
IrInstSrc *cond_br_inst = ir_build_cond_br(irb, parent_scope, node, is_non_null, ok_block, null_block, is_comptime);
ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, ok_block, end_block,
result_loc, is_comptime);
ir_set_cursor_at_end_and_append_block(irb, null_block);
IrInstSrc *null_result = ir_gen_node_extra(irb, op2_node, parent_scope, LValNone,
&peer_parent->peers.at(0)->base);
if (null_result == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
IrBasicBlockSrc *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);
IrInstSrc *unwrapped_ptr = ir_build_optional_unwrap_ptr(irb, parent_scope, node, maybe_ptr, false);
IrInstSrc *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr);
ir_build_end_expr(irb, parent_scope, node, unwrapped_payload, &peer_parent->peers.at(1)->base);
IrBasicBlockSrc *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);
IrInstSrc **incoming_values = heap::c_allocator.allocate<IrInstSrc *>(2);
incoming_values[0] = null_result;
incoming_values[1] = unwrapped_payload;
IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate<IrBasicBlockSrc *>(2);
incoming_blocks[0] = after_null_block;
incoming_blocks[1] = after_ok_block;
IrInstSrc *phi = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values, peer_parent);
return ir_lval_wrap(irb, parent_scope, phi, lval, result_loc);
}
static IrInstSrc *ir_gen_error_union(IrBuilderSrc *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;
IrInstSrc *err_set = ir_gen_node(irb, op1_node, parent_scope);
if (err_set == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
IrInstSrc *payload = ir_gen_node(irb, op2_node, parent_scope);
if (payload == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
return ir_build_error_union(irb, parent_scope, node, err_set, payload);
}
static IrInstSrc *ir_gen_bin_op(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) {
assert(node->type == NodeTypeBinOpExpr);
BinOpType bin_op_type = node->data.bin_op_expr.bin_op;
switch (bin_op_type) {
case BinOpTypeInvalid:
zig_unreachable();
case BinOpTypeAssign:
return ir_lval_wrap(irb, scope, ir_gen_assign(irb, scope, node), lval, result_loc);
case BinOpTypeAssignTimes:
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpMult), lval, result_loc);
case BinOpTypeAssignTimesWrap:
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpMultWrap), lval, result_loc);
case BinOpTypeAssignDiv:
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpDivUnspecified), lval, result_loc);
case BinOpTypeAssignMod:
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpRemUnspecified), lval, result_loc);
case BinOpTypeAssignPlus:
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpAdd), lval, result_loc);
case BinOpTypeAssignPlusWrap:
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpAddWrap), lval, result_loc);
case BinOpTypeAssignMinus:
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpSub), lval, result_loc);
case BinOpTypeAssignMinusWrap:
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpSubWrap), lval, result_loc);
case BinOpTypeAssignBitShiftLeft:
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftLeftLossy), lval, result_loc);
case BinOpTypeAssignBitShiftRight:
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc);
case BinOpTypeAssignBitAnd:
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBinAnd), lval, result_loc);
case BinOpTypeAssignBitXor:
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBinXor), lval, result_loc);
case BinOpTypeAssignBitOr:
return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBinOr), lval, result_loc);
case BinOpTypeBoolOr:
return ir_lval_wrap(irb, scope, ir_gen_bool_or(irb, scope, node), lval, result_loc);
case BinOpTypeBoolAnd:
return ir_lval_wrap(irb, scope, ir_gen_bool_and(irb, scope, node), lval, result_loc);
case BinOpTypeCmpEq:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpEq), lval, result_loc);
case BinOpTypeCmpNotEq:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpNotEq), lval, result_loc);
case BinOpTypeCmpLessThan:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessThan), lval, result_loc);
case BinOpTypeCmpGreaterThan:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterThan), lval, result_loc);
case BinOpTypeCmpLessOrEq:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessOrEq), lval, result_loc);
case BinOpTypeCmpGreaterOrEq:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterOrEq), lval, result_loc);
case BinOpTypeBinOr:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBinOr), lval, result_loc);
case BinOpTypeBinXor:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBinXor), lval, result_loc);
case BinOpTypeBinAnd:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBinAnd), lval, result_loc);
case BinOpTypeBitShiftLeft:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftLeftLossy), lval, result_loc);
case BinOpTypeBitShiftRight:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc);
case BinOpTypeAdd:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpAdd), lval, result_loc);
case BinOpTypeAddWrap:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpAddWrap), lval, result_loc);
case BinOpTypeSub:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpSub), lval, result_loc);
case BinOpTypeSubWrap:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpSubWrap), lval, result_loc);
case BinOpTypeMult:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpMult), lval, result_loc);
case BinOpTypeMultWrap:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpMultWrap), lval, result_loc);
case BinOpTypeDiv:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpDivUnspecified), lval, result_loc);
case BinOpTypeMod:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpRemUnspecified), lval, result_loc);
case BinOpTypeArrayCat:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayCat), lval, result_loc);
case BinOpTypeArrayMult:
return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayMult), lval, result_loc);
case BinOpTypeMergeErrorSets:
return ir_lval_wrap(irb, scope, ir_gen_merge_err_sets(irb, scope, node), lval, result_loc);
case BinOpTypeUnwrapOptional:
return ir_gen_orelse(irb, scope, node, lval, result_loc);
case BinOpTypeErrorUnion:
return ir_lval_wrap(irb, scope, ir_gen_error_union(irb, scope, node), lval, result_loc);
}
zig_unreachable();
}
static IrInstSrc *ir_gen_int_lit(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeIntLiteral);
RootStruct *root_struct = node->owner->data.structure.root_struct;
BigInt bigint;
token_number_literal_bigint(root_struct, &bigint, node->main_token);
return ir_build_const_bigint(irb, scope, node, bigint);
}
static IrInstSrc *ir_gen_float_lit(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
Error err;
assert(node->type == NodeTypeFloatLiteral);
RootStruct *root_struct = node->owner->data.structure.root_struct;
const char *source = buf_ptr(root_struct->source_code);
uint32_t byte_offset = root_struct->token_locs[node->main_token].offset;
BigFloat bigfloat;
if ((err = bigfloat_init_buf(&bigfloat, (const uint8_t *)source + byte_offset))) {
add_node_error(irb->codegen, node, buf_sprintf("float literal out of range of any type"));
return irb->codegen->invalid_inst_src;
}
return ir_build_const_bigfloat(irb, scope, node, bigfloat);
}
static IrInstSrc *ir_gen_char_lit(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
Error err;
assert(node->type == NodeTypeCharLiteral);
RootStruct *root_struct = node->owner->data.structure.root_struct;
const char *source = buf_ptr(root_struct->source_code);
uint32_t byte_offset = root_struct->token_locs[node->main_token].offset;
src_assert(source[byte_offset] == '\'', node);
byte_offset += 1;
uint32_t codepoint;
size_t bad_index;
if ((err = source_char_literal(source + byte_offset, &codepoint, &bad_index))) {
add_node_error(irb->codegen, node, buf_sprintf("invalid character"));
return irb->codegen->invalid_inst_src;
}
return ir_build_const_uint(irb, scope, node, codepoint);
}
static IrInstSrc *ir_gen_null_literal(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeNullLiteral);
return ir_build_const_null(irb, scope, node);
}
static IrInstSrc *ir_gen_symbol(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) {
Error err;
assert(node->type == NodeTypeIdentifier);
Buf *variable_name = node_identifier_buf(node);
if (buf_eql_str(variable_name, "_")) {
if (lval == LValAssign) {
IrInstSrcConst *const_instruction = ir_build_instruction<IrInstSrcConst>(irb, scope, node);
const_instruction->value = irb->codegen->pass1_arena->create<ZigValue>();
const_instruction->value->type = get_pointer_to_type(irb->codegen,
irb->codegen->builtin_types.entry_void, false);
const_instruction->value->special = ConstValSpecialStatic;
const_instruction->value->data.x_ptr.special = ConstPtrSpecialDiscard;
return &const_instruction->base;
} else {
add_node_error(irb->codegen, node, buf_sprintf("`_` may only be used to assign things to"));
return irb->codegen->invalid_inst_src;
}
}
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_inst_src;
}
assert(err == ErrorPrimitiveTypeNotFound);
} else {
IrInstSrc *value = ir_build_const_type(irb, scope, node, primitive_type);
if (lval == LValPtr || lval == LValAssign) {
return ir_build_ref_src(irb, scope, node, value);
} else {
return ir_expr_wrap(irb, scope, value, result_loc);
}
}
ScopeFnDef *crossed_fndef_scope;
ZigVar *var = find_variable(irb->codegen, scope, variable_name, &crossed_fndef_scope);
if (var) {
IrInstSrc *var_ptr = ir_build_var_ptr_x(irb, scope, node, var, crossed_fndef_scope);
if (lval == LValPtr || lval == LValAssign) {
return var_ptr;
} else {
return ir_expr_wrap(irb, scope, ir_build_load_ptr(irb, scope, node, var_ptr), result_loc);
}
}
Tld *tld = find_decl(irb->codegen, scope, variable_name);
if (tld) {
IrInstSrc *decl_ref = ir_build_decl_ref(irb, scope, node, tld, lval);
if (lval == LValPtr || lval == LValAssign) {
return decl_ref;
} else {
return ir_expr_wrap(irb, scope, decl_ref, result_loc);
}
}
if (get_container_scope(node->owner)->any_imports_failed) {
// skip the error message since we had a failing import in this file
// if an import breaks we don't need redundant undeclared identifier errors
return irb->codegen->invalid_inst_src;
}
return ir_build_undeclared_identifier(irb, scope, node, variable_name);
}
static IrInstSrc *ir_gen_array_access(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
ResultLoc *result_loc)
{
assert(node->type == NodeTypeArrayAccessExpr);
AstNode *array_ref_node = node->data.array_access_expr.array_ref_expr;
IrInstSrc *array_ref_instruction = ir_gen_node_extra(irb, array_ref_node, scope, LValPtr, nullptr);
if (array_ref_instruction == irb->codegen->invalid_inst_src)
return array_ref_instruction;
// Create an usize-typed result location to hold the subscript value, this
// makes it possible for the compiler to infer the subscript expression type
// if needed
IrInstSrc *usize_type_inst = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize);
ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, usize_type_inst, no_result_loc());
AstNode *subscript_node = node->data.array_access_expr.subscript;
IrInstSrc *subscript_value = ir_gen_node_extra(irb, subscript_node, scope, LValNone, &result_loc_cast->base);
if (subscript_value == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
IrInstSrc *subscript_instruction = ir_build_implicit_cast(irb, scope, subscript_node, subscript_value, result_loc_cast);
IrInstSrc *ptr_instruction = ir_build_elem_ptr(irb, scope, node, array_ref_instruction,
subscript_instruction, true, PtrLenSingle, nullptr);
if (lval == LValPtr || lval == LValAssign)
return ptr_instruction;
IrInstSrc *load_ptr = ir_build_load_ptr(irb, scope, node, ptr_instruction);
return ir_expr_wrap(irb, scope, load_ptr, result_loc);
}
static IrInstSrc *ir_gen_field_access(IrBuilderSrc *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;
IrInstSrc *container_ref_instruction = ir_gen_node_extra(irb, container_ref_node, scope, LValPtr, nullptr);
if (container_ref_instruction == irb->codegen->invalid_inst_src)
return container_ref_instruction;
return ir_build_field_ptr(irb, scope, node, container_ref_instruction, field_name, false);
}
static IrInstSrc *ir_gen_overflow_op(IrBuilderSrc *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);
IrInstSrc *type_value = ir_gen_node(irb, type_node, scope);
if (type_value == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
IrInstSrc *op1 = ir_gen_node(irb, op1_node, scope);
if (op1 == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
IrInstSrc *op2 = ir_gen_node(irb, op2_node, scope);
if (op2 == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
IrInstSrc *result_ptr = ir_gen_node(irb, result_ptr_node, scope);
if (result_ptr == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
return ir_build_overflow_op_src(irb, scope, node, op, type_value, op1, op2, result_ptr);
}
static IrInstSrc *ir_gen_mul_add(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeFnCallExpr);
AstNode *type_node = node->data.fn_call_expr.params.at(0);
AstNode *op1_node = node->data.fn_call_expr.params.at(1);
AstNode *op2_node = node->data.fn_call_expr.params.at(2);
AstNode *op3_node = node->data.fn_call_expr.params.at(3);
IrInstSrc *type_value = ir_gen_node(irb, type_node, scope);
if (type_value == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
IrInstSrc *op1 = ir_gen_node(irb, op1_node, scope);
if (op1 == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
IrInstSrc *op2 = ir_gen_node(irb, op2_node, scope);
if (op2 == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
IrInstSrc *op3 = ir_gen_node(irb, op3_node, scope);
if (op3 == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
return ir_build_mul_add_src(irb, scope, node, type_value, op1, op2, op3);
}
static IrInstSrc *ir_gen_this(IrBuilderSrc *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 IrInstSrc *ir_gen_async_call(IrBuilderSrc *irb, Scope *scope, AstNode *await_node, AstNode *call_node,
LVal lval, ResultLoc *result_loc)
{
if (call_node->data.fn_call_expr.params.length != 4) {
add_node_error(irb->codegen, call_node,
buf_sprintf("expected 4 arguments, found %" ZIG_PRI_usize,
call_node->data.fn_call_expr.params.length));
return irb->codegen->invalid_inst_src;
}
AstNode *bytes_node = call_node->data.fn_call_expr.params.at(0);
IrInstSrc *bytes = ir_gen_node(irb, bytes_node, scope);
if (bytes == irb->codegen->invalid_inst_src)
return bytes;
AstNode *ret_ptr_node = call_node->data.fn_call_expr.params.at(1);
IrInstSrc *ret_ptr = ir_gen_node(irb, ret_ptr_node, scope);
if (ret_ptr == irb->codegen->invalid_inst_src)
return ret_ptr;
AstNode *fn_ref_node = call_node->data.fn_call_expr.params.at(2);
IrInstSrc *fn_ref = ir_gen_node(irb, fn_ref_node, scope);
if (fn_ref == irb->codegen->invalid_inst_src)
return fn_ref;
CallModifier modifier = (await_node == nullptr) ? CallModifierAsync : CallModifierNone;
bool is_async_call_builtin = true;
AstNode *args_node = call_node->data.fn_call_expr.params.at(3);
if (args_node->type == NodeTypeContainerInitExpr) {
if (args_node->data.container_init_expr.kind == ContainerInitKindArray ||
args_node->data.container_init_expr.entries.length == 0)
{
size_t arg_count = args_node->data.container_init_expr.entries.length;
IrInstSrc **args = heap::c_allocator.allocate<IrInstSrc*>(arg_count);
for (size_t i = 0; i < arg_count; i += 1) {
AstNode *arg_node = args_node->data.container_init_expr.entries.at(i);
IrInstSrc *arg = ir_gen_node(irb, arg_node, scope);
if (arg == irb->codegen->invalid_inst_src)
return arg;
args[i] = arg;
}
IrInstSrc *call = ir_build_call_src(irb, scope, call_node, nullptr, fn_ref, arg_count, args,
ret_ptr, modifier, is_async_call_builtin, bytes, result_loc);
return ir_lval_wrap(irb, scope, call, lval, result_loc);
} else {
exec_add_error_node(irb->codegen, irb->exec, args_node,
buf_sprintf("TODO: @asyncCall with anon struct literal"));
return irb->codegen->invalid_inst_src;
}
}
IrInstSrc *args = ir_gen_node(irb, args_node, scope);
if (args == irb->codegen->invalid_inst_src)
return args;
IrInstSrc *call = ir_build_async_call_extra(irb, scope, call_node, modifier, fn_ref, ret_ptr, bytes, args, result_loc);
return ir_lval_wrap(irb, scope, call, lval, result_loc);
}
static IrInstSrc *ir_gen_fn_call_with_args(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
AstNode *fn_ref_node, CallModifier modifier, IrInstSrc *options,
AstNode **args_ptr, size_t args_len, LVal lval, ResultLoc *result_loc)
{
IrInstSrc *fn_ref = ir_gen_node(irb, fn_ref_node, scope);
if (fn_ref == irb->codegen->invalid_inst_src)
return fn_ref;
IrInstSrc *fn_type = ir_build_typeof_1(irb, scope, source_node, fn_ref);
IrInstSrc **args = heap::c_allocator.allocate<IrInstSrc*>(args_len);
for (size_t i = 0; i < args_len; i += 1) {
AstNode *arg_node = args_ptr[i];
IrInstSrc *arg_index = ir_build_const_usize(irb, scope, arg_node, i);
IrInstSrc *arg_type = ir_build_arg_type(irb, scope, source_node, fn_type, arg_index, true);
ResultLoc *no_result = no_result_loc();
ir_build_reset_result(irb, scope, source_node, no_result);
ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, arg_type, no_result);
IrInstSrc *arg = ir_gen_node_extra(irb, arg_node, scope, LValNone, &result_loc_cast->base);
if (arg == irb->codegen->invalid_inst_src)
return arg;
args[i] = ir_build_implicit_cast(irb, scope, arg_node, arg, result_loc_cast);
}
IrInstSrc *fn_call;
if (options != nullptr) {
fn_call = ir_build_call_args(irb, scope, source_node, options, fn_ref, args, args_len, result_loc);
} else {
fn_call = ir_build_call_src(irb, scope, source_node, nullptr, fn_ref, args_len, args, nullptr,
modifier, false, nullptr, result_loc);
}
return ir_lval_wrap(irb, scope, fn_call, lval, result_loc);
}
static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
ResultLoc *result_loc)
{
assert(node->type == NodeTypeFnCallExpr);
AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
Buf *name = node_identifier_buf(fn_ref_expr);
auto entry = irb->codegen->builtin_fn_table.maybe_get(name);
if (!entry) {
add_node_error(irb->codegen, node,
buf_sprintf("invalid builtin function: '%s'", buf_ptr(name)));
return irb->codegen->invalid_inst_src;
}
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 " argument(s), found %" ZIG_PRI_usize,
builtin_fn->param_count, actual_param_count));
return irb->codegen->invalid_inst_src;
}
switch (builtin_fn->id) {
case BuiltinFnIdInvalid:
zig_unreachable();
case BuiltinFnIdTypeof:
{
Scope *sub_scope = create_typeof_scope(irb->codegen, node, scope);
size_t arg_count = node->data.fn_call_expr.params.length;
IrInstSrc *type_of;
if (arg_count == 0) {
add_node_error(irb->codegen, node,
buf_sprintf("expected at least 1 argument, found 0"));
return irb->codegen->invalid_inst_src;
} else if (arg_count == 1) {
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, sub_scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
type_of = ir_build_typeof_1(irb, scope, node, arg0_value);
} else {
IrInstSrc **args = heap::c_allocator.allocate<IrInstSrc*>(arg_count);
for (size_t i = 0; i < arg_count; i += 1) {
AstNode *arg_node = node->data.fn_call_expr.params.at(i);
IrInstSrc *arg = ir_gen_node(irb, arg_node, sub_scope);
if (arg == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
args[i] = arg;
}
type_of = ir_build_typeof_n(irb, scope, node, args, arg_count);
}
return ir_lval_wrap(irb, scope, type_of, lval, result_loc);
}
case BuiltinFnIdSetCold:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
IrInstSrc *set_cold = ir_build_set_cold(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, set_cold, lval, result_loc);
}
case BuiltinFnIdSetRuntimeSafety:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
IrInstSrc *set_safety = ir_build_set_runtime_safety(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, set_safety, lval, result_loc);
}
case BuiltinFnIdSetFloatMode:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
IrInstSrc *set_float_mode = ir_build_set_float_mode(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, set_float_mode, lval, result_loc);
}
case BuiltinFnIdSizeof:
case BuiltinFnIdBitSizeof:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
IrInstSrc *size_of = ir_build_size_of(irb, scope, node, arg0_value, builtin_fn->id == BuiltinFnIdBitSizeof);
return ir_lval_wrap(irb, scope, size_of, lval, result_loc);
}
case BuiltinFnIdImport:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
IrInstSrc *import = ir_build_import(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, import, lval, result_loc);
}
case BuiltinFnIdCImport:
{
IrInstSrc *c_import = ir_build_c_import(irb, scope, node);
return ir_lval_wrap(irb, scope, c_import, lval, result_loc);
}
case BuiltinFnIdCInclude:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
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_inst_src;
}
IrInstSrc *c_include = ir_build_c_include(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, c_include, lval, result_loc);
}
case BuiltinFnIdCDefine:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
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_inst_src;
}
IrInstSrc *c_define = ir_build_c_define(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, c_define, lval, result_loc);
}
case BuiltinFnIdCUndef:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
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_inst_src;
}
IrInstSrc *c_undef = ir_build_c_undef(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, c_undef, lval, result_loc);
}
case BuiltinFnIdCompileErr:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
IrInstSrc *compile_err = ir_build_compile_err(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, compile_err, lval, result_loc);
}
case BuiltinFnIdCompileLog:
{
IrInstSrc **args = heap::c_allocator.allocate<IrInstSrc*>(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_inst_src)
return irb->codegen->invalid_inst_src;
}
IrInstSrc *compile_log = ir_build_compile_log(irb, scope, node, actual_param_count, args);
return ir_lval_wrap(irb, scope, compile_log, lval, result_loc);
}
case BuiltinFnIdErrName:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
IrInstSrc *err_name = ir_build_err_name(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, err_name, lval, result_loc);
}
case BuiltinFnIdEmbedFile:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
IrInstSrc *embed_file = ir_build_embed_file(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, embed_file, lval, result_loc);
}
case BuiltinFnIdCmpxchgWeak:
case BuiltinFnIdCmpxchgStrong:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope);
if (arg2_value == irb->codegen->invalid_inst_src)
return arg2_value;
AstNode *arg3_node = node->data.fn_call_expr.params.at(3);
IrInstSrc *arg3_value = ir_gen_node(irb, arg3_node, scope);
if (arg3_value == irb->codegen->invalid_inst_src)
return arg3_value;
AstNode *arg4_node = node->data.fn_call_expr.params.at(4);
IrInstSrc *arg4_value = ir_gen_node(irb, arg4_node, scope);
if (arg4_value == irb->codegen->invalid_inst_src)
return arg4_value;
AstNode *arg5_node = node->data.fn_call_expr.params.at(5);
IrInstSrc *arg5_value = ir_gen_node(irb, arg5_node, scope);
if (arg5_value == irb->codegen->invalid_inst_src)
return arg5_value;
IrInstSrc *cmpxchg = ir_build_cmpxchg_src(irb, scope, node, arg0_value, arg1_value,
arg2_value, arg3_value, arg4_value, arg5_value, (builtin_fn->id == BuiltinFnIdCmpxchgWeak),
result_loc);
return ir_lval_wrap(irb, scope, cmpxchg, lval, result_loc);
}
case BuiltinFnIdFence:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
IrInstSrc *fence = ir_build_fence(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, fence, lval, result_loc);
}
case BuiltinFnIdReduce:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
IrInstSrc *reduce = ir_build_reduce(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, reduce, lval, result_loc);
}
case BuiltinFnIdDivExact:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivExact, arg0_value, arg1_value, true);
return ir_lval_wrap(irb, scope, bin_op, lval, result_loc);
}
case BuiltinFnIdDivTrunc:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivTrunc, arg0_value, arg1_value, true);
return ir_lval_wrap(irb, scope, bin_op, lval, result_loc);
}
case BuiltinFnIdDivFloor:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivFloor, arg0_value, arg1_value, true);
return ir_lval_wrap(irb, scope, bin_op, lval, result_loc);
}
case BuiltinFnIdRem:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpRemRem, arg0_value, arg1_value, true);
return ir_lval_wrap(irb, scope, bin_op, lval, result_loc);
}
case BuiltinFnIdMod:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpRemMod, arg0_value, arg1_value, true);
return ir_lval_wrap(irb, scope, bin_op, lval, result_loc);
}
case BuiltinFnIdSqrt:
case BuiltinFnIdSin:
case BuiltinFnIdCos:
case BuiltinFnIdExp:
case BuiltinFnIdExp2:
case BuiltinFnIdLog:
case BuiltinFnIdLog2:
case BuiltinFnIdLog10:
case BuiltinFnIdFabs:
case BuiltinFnIdFloor:
case BuiltinFnIdCeil:
case BuiltinFnIdTrunc:
case BuiltinFnIdNearbyInt:
case BuiltinFnIdRound:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
IrInstSrc *inst = ir_build_float_op_src(irb, scope, node, arg0_value, builtin_fn->id);
return ir_lval_wrap(irb, scope, inst, lval, result_loc);
}
case BuiltinFnIdTruncate:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
IrInstSrc *truncate = ir_build_truncate(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, truncate, lval, result_loc);
}
case BuiltinFnIdIntCast:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
IrInstSrc *result = ir_build_int_cast(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, result, lval, result_loc);
}
case BuiltinFnIdFloatCast:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
IrInstSrc *result = ir_build_float_cast(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, result, lval, result_loc);
}
case BuiltinFnIdErrSetCast:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
IrInstSrc *result = ir_build_err_set_cast(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, result, lval, result_loc);
}
case BuiltinFnIdIntToFloat:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
IrInstSrc *result = ir_build_int_to_float(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, result, lval, result_loc);
}
case BuiltinFnIdFloatToInt:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
IrInstSrc *result = ir_build_float_to_int(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, result, lval, result_loc);
}
case BuiltinFnIdErrToInt:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
IrInstSrc *result = ir_build_err_to_int_src(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, result, lval, result_loc);
}
case BuiltinFnIdIntToErr:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
IrInstSrc *result = ir_build_int_to_err_src(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, result, lval, result_loc);
}
case BuiltinFnIdBoolToInt:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
IrInstSrc *result = ir_build_bool_to_int(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, result, lval, result_loc);
}
case BuiltinFnIdVectorType:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
IrInstSrc *vector_type = ir_build_vector_type(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, vector_type, lval, result_loc);
}
case BuiltinFnIdShuffle:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope);
if (arg2_value == irb->codegen->invalid_inst_src)
return arg2_value;
AstNode *arg3_node = node->data.fn_call_expr.params.at(3);
IrInstSrc *arg3_value = ir_gen_node(irb, arg3_node, scope);
if (arg3_value == irb->codegen->invalid_inst_src)
return arg3_value;
IrInstSrc *shuffle_vector = ir_build_shuffle_vector(irb, scope, node,
arg0_value, arg1_value, arg2_value, arg3_value);
return ir_lval_wrap(irb, scope, shuffle_vector, lval, result_loc);
}
case BuiltinFnIdSplat:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
IrInstSrc *splat = ir_build_splat_src(irb, scope, node,
arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, splat, lval, result_loc);
}
case BuiltinFnIdMemcpy:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope);
if (arg2_value == irb->codegen->invalid_inst_src)
return arg2_value;
IrInstSrc *ir_memcpy = ir_build_memcpy_src(irb, scope, node, arg0_value, arg1_value, arg2_value);
return ir_lval_wrap(irb, scope, ir_memcpy, lval, result_loc);
}
case BuiltinFnIdMemset:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope);
if (arg2_value == irb->codegen->invalid_inst_src)
return arg2_value;
IrInstSrc *ir_memset = ir_build_memset_src(irb, scope, node, arg0_value, arg1_value, arg2_value);
return ir_lval_wrap(irb, scope, ir_memset, lval, result_loc);
}
case BuiltinFnIdWasmMemorySize:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
IrInstSrc *ir_wasm_memory_size = ir_build_wasm_memory_size_src(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, ir_wasm_memory_size, lval, result_loc);
}
case BuiltinFnIdWasmMemoryGrow:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
IrInstSrc *ir_wasm_memory_grow = ir_build_wasm_memory_grow_src(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, ir_wasm_memory_grow, lval, result_loc);
}
case BuiltinFnIdField:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node_extra(irb, arg0_node, scope, LValPtr, nullptr);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
IrInstSrc *ptr_instruction = ir_build_field_ptr_instruction(irb, scope, node,
arg0_value, arg1_value, false);
if (lval == LValPtr || lval == LValAssign)
return ptr_instruction;
IrInstSrc *load_ptr = ir_build_load_ptr(irb, scope, node, ptr_instruction);
return ir_expr_wrap(irb, scope, load_ptr, result_loc);
}
case BuiltinFnIdHasField:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
IrInstSrc *type_info = ir_build_has_field(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, type_info, lval, result_loc);
}
case BuiltinFnIdTypeInfo:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
IrInstSrc *type_info = ir_build_type_info(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, type_info, lval, result_loc);
}
case BuiltinFnIdType:
{
AstNode *arg_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg = ir_gen_node(irb, arg_node, scope);
if (arg == irb->codegen->invalid_inst_src)
return arg;
IrInstSrc *type = ir_build_type(irb, scope, node, arg);
return ir_lval_wrap(irb, scope, type, lval, result_loc);
}
case BuiltinFnIdBreakpoint:
return ir_lval_wrap(irb, scope, ir_build_breakpoint(irb, scope, node), lval, result_loc);
case BuiltinFnIdReturnAddress:
return ir_lval_wrap(irb, scope, ir_build_return_address_src(irb, scope, node), lval, result_loc);
case BuiltinFnIdFrameAddress:
return ir_lval_wrap(irb, scope, ir_build_frame_address_src(irb, scope, node), lval, result_loc);
case BuiltinFnIdFrameHandle:
if (!irb->exec->fn_entry) {
add_node_error(irb->codegen, node, buf_sprintf("@frame() called outside of function definition"));
return irb->codegen->invalid_inst_src;
}
return ir_lval_wrap(irb, scope, ir_build_handle_src(irb, scope, node), lval, result_loc);
case BuiltinFnIdFrameType: {
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
IrInstSrc *frame_type = ir_build_frame_type(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, frame_type, lval, result_loc);
}
case BuiltinFnIdFrameSize: {
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
IrInstSrc *frame_size = ir_build_frame_size_src(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, frame_size, lval, result_loc);
}
case BuiltinFnIdAlignOf:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
IrInstSrc *align_of = ir_build_align_of(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, align_of, lval, result_loc);
}
case BuiltinFnIdAddWithOverflow:
return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpAdd), lval, result_loc);
case BuiltinFnIdSubWithOverflow:
return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpSub), lval, result_loc);
case BuiltinFnIdMulWithOverflow:
return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul), lval, result_loc);
case BuiltinFnIdShlWithOverflow:
return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl), lval, result_loc);
case BuiltinFnIdMulAdd:
return ir_lval_wrap(irb, scope, ir_gen_mul_add(irb, scope, node), lval, result_loc);
case BuiltinFnIdTypeName:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
IrInstSrc *type_name = ir_build_type_name(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, type_name, lval, result_loc);
}
case BuiltinFnIdPanic:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
IrInstSrc *panic = ir_build_panic_src(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, panic, lval, result_loc);
}
case BuiltinFnIdPtrCast:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
IrInstSrc *ptr_cast = ir_build_ptr_cast_src(irb, scope, node, arg0_value, arg1_value, true);
return ir_lval_wrap(irb, scope, ptr_cast, lval, result_loc);
}
case BuiltinFnIdBitCast:
{
AstNode *dest_type_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *dest_type = ir_gen_node(irb, dest_type_node, scope);
if (dest_type == irb->codegen->invalid_inst_src)
return dest_type;
ResultLocBitCast *result_loc_bit_cast = heap::c_allocator.create<ResultLocBitCast>();
result_loc_bit_cast->base.id = ResultLocIdBitCast;
result_loc_bit_cast->base.source_instruction = dest_type;
result_loc_bit_cast->base.allow_write_through_const = result_loc->allow_write_through_const;
ir_ref_instruction(dest_type, irb->current_basic_block);
result_loc_bit_cast->parent = result_loc;
ir_build_reset_result(irb, scope, node, &result_loc_bit_cast->base);
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node_extra(irb, arg1_node, scope, LValNone,
&result_loc_bit_cast->base);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
IrInstSrc *bitcast = ir_build_bit_cast_src(irb, scope, arg1_node, arg1_value, result_loc_bit_cast);
return ir_lval_wrap(irb, scope, bitcast, lval, result_loc);
}
case BuiltinFnIdAs:
{
AstNode *dest_type_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *dest_type = ir_gen_node(irb, dest_type_node, scope);
if (dest_type == irb->codegen->invalid_inst_src)
return dest_type;
ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, dest_type, result_loc);
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node_extra(irb, arg1_node, scope, LValNone,
&result_loc_cast->base);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
IrInstSrc *result = ir_build_implicit_cast(irb, scope, node, arg1_value, result_loc_cast);
return ir_lval_wrap(irb, scope, result, lval, result_loc);
}
case BuiltinFnIdIntToPtr:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
IrInstSrc *int_to_ptr = ir_build_int_to_ptr_src(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, int_to_ptr, lval, result_loc);
}
case BuiltinFnIdPtrToInt:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
IrInstSrc *ptr_to_int = ir_build_ptr_to_int_src(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, ptr_to_int, lval, result_loc);
}
case BuiltinFnIdTagName:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
IrInstSrc *tag_name = ir_build_tag_name_src(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, tag_name, lval, result_loc);
}
case BuiltinFnIdFieldParentPtr:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope);
if (arg2_value == irb->codegen->invalid_inst_src)
return arg2_value;
IrInstSrc *field_parent_ptr = ir_build_field_parent_ptr_src(irb, scope, node,
arg0_value, arg1_value, arg2_value);
return ir_lval_wrap(irb, scope, field_parent_ptr, lval, result_loc);
}
case BuiltinFnIdByteOffsetOf:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
IrInstSrc *offset_of = ir_build_byte_offset_of(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, offset_of, lval, result_loc);
}
case BuiltinFnIdBitOffsetOf:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
IrInstSrc *offset_of = ir_build_bit_offset_of(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, offset_of, lval, result_loc);
}
case BuiltinFnIdCall: {
// Cast the options parameter to the options type
ZigType *options_type = get_builtin_type(irb->codegen, "CallOptions");
IrInstSrc *options_type_inst = ir_build_const_type(irb, scope, node, options_type);
ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, options_type_inst, no_result_loc());
AstNode *options_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *options_inner = ir_gen_node_extra(irb, options_node, scope,
LValNone, &result_loc_cast->base);
if (options_inner == irb->codegen->invalid_inst_src)
return options_inner;
IrInstSrc *options = ir_build_implicit_cast(irb, scope, options_node, options_inner, result_loc_cast);
AstNode *fn_ref_node = node->data.fn_call_expr.params.at(1);
AstNode *args_node = node->data.fn_call_expr.params.at(2);
if (args_node->type == NodeTypeContainerInitExpr) {
if (args_node->data.container_init_expr.kind == ContainerInitKindArray ||
args_node->data.container_init_expr.entries.length == 0)
{
return ir_gen_fn_call_with_args(irb, scope, node,
fn_ref_node, CallModifierNone, options,
args_node->data.container_init_expr.entries.items,
args_node->data.container_init_expr.entries.length,
lval, result_loc);
} else {
exec_add_error_node(irb->codegen, irb->exec, args_node,
buf_sprintf("TODO: @call with anon struct literal"));
return irb->codegen->invalid_inst_src;
}
} else {
IrInstSrc *fn_ref = ir_gen_node(irb, fn_ref_node, scope);
if (fn_ref == irb->codegen->invalid_inst_src)
return fn_ref;
IrInstSrc *args = ir_gen_node(irb, args_node, scope);
if (args == irb->codegen->invalid_inst_src)
return args;
IrInstSrc *call = ir_build_call_extra(irb, scope, node, options, fn_ref, args, result_loc);
return ir_lval_wrap(irb, scope, call, lval, result_loc);
}
}
case BuiltinFnIdAsyncCall:
return ir_gen_async_call(irb, scope, nullptr, node, lval, result_loc);
case BuiltinFnIdShlExact:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftLeftExact, arg0_value, arg1_value, true);
return ir_lval_wrap(irb, scope, bin_op, lval, result_loc);
}
case BuiltinFnIdShrExact:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftRightExact, arg0_value, arg1_value, true);
return ir_lval_wrap(irb, scope, bin_op, lval, result_loc);
}
case BuiltinFnIdSetEvalBranchQuota:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
IrInstSrc *set_eval_branch_quota = ir_build_set_eval_branch_quota(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, set_eval_branch_quota, lval, result_loc);
}
case BuiltinFnIdAlignCast:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
IrInstSrc *align_cast = ir_build_align_cast_src(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, align_cast, lval, result_loc);
}
case BuiltinFnIdThis:
{
IrInstSrc *this_inst = ir_gen_this(irb, scope, node);
return ir_lval_wrap(irb, scope, this_inst, lval, result_loc);
}
case BuiltinFnIdSetAlignStack:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
IrInstSrc *set_align_stack = ir_build_set_align_stack(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, set_align_stack, lval, result_loc);
}
case BuiltinFnIdExport:
{
// Cast the options parameter to the options type
ZigType *options_type = get_builtin_type(irb->codegen, "ExportOptions");
IrInstSrc *options_type_inst = ir_build_const_type(irb, scope, node, options_type);
ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, options_type_inst, no_result_loc());
AstNode *target_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *target_value = ir_gen_node(irb, target_node, scope);
if (target_value == irb->codegen->invalid_inst_src)
return target_value;
AstNode *options_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *options_value = ir_gen_node_extra(irb, options_node,
scope, LValNone, &result_loc_cast->base);
if (options_value == irb->codegen->invalid_inst_src)
return options_value;
IrInstSrc *casted_options_value = ir_build_implicit_cast(
irb, scope, options_node, options_value, result_loc_cast);
IrInstSrc *ir_export = ir_build_export(irb, scope, node, target_value, casted_options_value);
return ir_lval_wrap(irb, scope, ir_export, lval, result_loc);
}
case BuiltinFnIdExtern:
{
// Cast the options parameter to the options type
ZigType *options_type = get_builtin_type(irb->codegen, "ExternOptions");
IrInstSrc *options_type_inst = ir_build_const_type(irb, scope, node, options_type);
ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, options_type_inst, no_result_loc());
AstNode *type_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *type_value = ir_gen_node(irb, type_node, scope);
if (type_value == irb->codegen->invalid_inst_src)
return type_value;
AstNode *options_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *options_value = ir_gen_node_extra(irb, options_node,
scope, LValNone, &result_loc_cast->base);
if (options_value == irb->codegen->invalid_inst_src)
return options_value;
IrInstSrc *casted_options_value = ir_build_implicit_cast(
irb, scope, options_node, options_value, result_loc_cast);
IrInstSrc *ir_extern = ir_build_extern(irb, scope, node, type_value, casted_options_value);
return ir_lval_wrap(irb, scope, ir_extern, lval, result_loc);
}
case BuiltinFnIdErrorReturnTrace:
{
IrInstSrc *error_return_trace = ir_build_error_return_trace_src(irb, scope, node,
IrInstErrorReturnTraceNull);
return ir_lval_wrap(irb, scope, error_return_trace, lval, result_loc);
}
case BuiltinFnIdAtomicRmw:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope);
if (arg2_value == irb->codegen->invalid_inst_src)
return arg2_value;
AstNode *arg3_node = node->data.fn_call_expr.params.at(3);
IrInstSrc *arg3_value = ir_gen_node(irb, arg3_node, scope);
if (arg3_value == irb->codegen->invalid_inst_src)
return arg3_value;
AstNode *arg4_node = node->data.fn_call_expr.params.at(4);
IrInstSrc *arg4_value = ir_gen_node(irb, arg4_node, scope);
if (arg4_value == irb->codegen->invalid_inst_src)
return arg4_value;
IrInstSrc *inst = ir_build_atomic_rmw_src(irb, scope, node,
arg0_value, arg1_value, arg2_value, arg3_value, arg4_value);
return ir_lval_wrap(irb, scope, inst, lval, result_loc);
}
case BuiltinFnIdAtomicLoad:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope);
if (arg2_value == irb->codegen->invalid_inst_src)
return arg2_value;
IrInstSrc *inst = ir_build_atomic_load_src(irb, scope, node, arg0_value, arg1_value, arg2_value);
return ir_lval_wrap(irb, scope, inst, lval, result_loc);
}
case BuiltinFnIdAtomicStore:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope);
if (arg2_value == irb->codegen->invalid_inst_src)
return arg2_value;
AstNode *arg3_node = node->data.fn_call_expr.params.at(3);
IrInstSrc *arg3_value = ir_gen_node(irb, arg3_node, scope);
if (arg3_value == irb->codegen->invalid_inst_src)
return arg3_value;
IrInstSrc *inst = ir_build_atomic_store_src(irb, scope, node, arg0_value, arg1_value,
arg2_value, arg3_value);
return ir_lval_wrap(irb, scope, inst, lval, result_loc);
}
case BuiltinFnIdIntToEnum:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
IrInstSrc *result = ir_build_int_to_enum_src(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, result, lval, result_loc);
}
case BuiltinFnIdEnumToInt:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
IrInstSrc *result = ir_build_enum_to_int(irb, scope, node, arg0_value);
return ir_lval_wrap(irb, scope, result, lval, result_loc);
}
case BuiltinFnIdCtz:
case BuiltinFnIdPopCount:
case BuiltinFnIdClz:
case BuiltinFnIdBswap:
case BuiltinFnIdBitReverse:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
IrInstSrc *result;
switch (builtin_fn->id) {
case BuiltinFnIdCtz:
result = ir_build_ctz(irb, scope, node, arg0_value, arg1_value);
break;
case BuiltinFnIdPopCount:
result = ir_build_pop_count(irb, scope, node, arg0_value, arg1_value);
break;
case BuiltinFnIdClz:
result = ir_build_clz(irb, scope, node, arg0_value, arg1_value);
break;
case BuiltinFnIdBswap:
result = ir_build_bswap(irb, scope, node, arg0_value, arg1_value);
break;
case BuiltinFnIdBitReverse:
result = ir_build_bit_reverse(irb, scope, node, arg0_value, arg1_value);
break;
default:
zig_unreachable();
}
return ir_lval_wrap(irb, scope, result, lval, result_loc);
}
case BuiltinFnIdHasDecl:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_inst_src)
return arg0_value;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_inst_src)
return arg1_value;
IrInstSrc *has_decl = ir_build_has_decl(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, has_decl, lval, result_loc);
}
case BuiltinFnIdUnionInit:
{
AstNode *union_type_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *union_type_inst = ir_gen_node(irb, union_type_node, scope);
if (union_type_inst == irb->codegen->invalid_inst_src)
return union_type_inst;
AstNode *name_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *name_inst = ir_gen_node(irb, name_node, scope);
if (name_inst == irb->codegen->invalid_inst_src)
return name_inst;
AstNode *init_node = node->data.fn_call_expr.params.at(2);
return ir_gen_union_init_expr(irb, scope, node, union_type_inst, name_inst, init_node,
lval, result_loc);
}
case BuiltinFnIdSrc:
{
IrInstSrc *src_inst = ir_build_src(irb, scope, node);
return ir_lval_wrap(irb, scope, src_inst, lval, result_loc);
}
}
zig_unreachable();
}
static ScopeNoSuspend *get_scope_nosuspend(Scope *scope) {
while (scope) {
if (scope->id == ScopeIdNoSuspend)
return (ScopeNoSuspend *)scope;
if (scope->id == ScopeIdFnDef)
return nullptr;
scope = scope->parent;
}
return nullptr;
}
static IrInstSrc *ir_gen_fn_call(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
ResultLoc *result_loc)
{
assert(node->type == NodeTypeFnCallExpr);
if (node->data.fn_call_expr.modifier == CallModifierBuiltin)
return ir_gen_builtin_fn_call(irb, scope, node, lval, result_loc);
bool is_nosuspend = get_scope_nosuspend(scope) != nullptr;
CallModifier modifier = node->data.fn_call_expr.modifier;
if (is_nosuspend && modifier != CallModifierAsync) {
modifier = CallModifierNoSuspend;
}
AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr;
return ir_gen_fn_call_with_args(irb, scope, node, fn_ref_node, modifier,
nullptr, node->data.fn_call_expr.params.items, node->data.fn_call_expr.params.length, lval, result_loc);
}
static IrInstSrc *ir_gen_if_bool_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
ResultLoc *result_loc)
{
assert(node->type == NodeTypeIfBoolExpr);
IrInstSrc *condition = ir_gen_node(irb, node->data.if_bool_expr.condition, scope);
if (condition == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
IrInstSrc *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;
IrBasicBlockSrc *then_block = ir_create_basic_block(irb, scope, "Then");
IrBasicBlockSrc *else_block = ir_create_basic_block(irb, scope, "Else");
IrBasicBlockSrc *endif_block = ir_create_basic_block(irb, scope, "EndIf");
IrInstSrc *cond_br_inst = ir_build_cond_br(irb, scope, node, condition,
then_block, else_block, is_comptime);
ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, else_block, endif_block,
result_loc, is_comptime);
ir_set_cursor_at_end_and_append_block(irb, then_block);
Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
IrInstSrc *then_expr_result = ir_gen_node_extra(irb, then_node, subexpr_scope, lval,
&peer_parent->peers.at(0)->base);
if (then_expr_result == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
IrBasicBlockSrc *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);
IrInstSrc *else_expr_result;
if (else_node) {
else_expr_result = ir_gen_node_extra(irb, else_node, subexpr_scope, lval, &peer_parent->peers.at(1)->base);
if (else_expr_result == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
} else {
else_expr_result = ir_build_const_void(irb, scope, node);
ir_build_end_expr(irb, scope, node, else_expr_result, &peer_parent->peers.at(1)->base);
}
IrBasicBlockSrc *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);
IrInstSrc **incoming_values = heap::c_allocator.allocate<IrInstSrc *>(2);
incoming_values[0] = then_expr_result;
incoming_values[1] = else_expr_result;
IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate<IrBasicBlockSrc *>(2);
incoming_blocks[0] = after_then_block;
incoming_blocks[1] = after_else_block;
IrInstSrc *phi = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, peer_parent);
return ir_expr_wrap(irb, scope, phi, result_loc);
}
static IrInstSrc *ir_gen_prefix_op_id_lval(IrBuilderSrc *irb, Scope *scope, AstNode *node, IrUnOp op_id, LVal lval) {
assert(node->type == NodeTypePrefixOpExpr);
AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
IrInstSrc *value = ir_gen_node_extra(irb, expr_node, scope, lval, nullptr);
if (value == irb->codegen->invalid_inst_src)
return value;
return ir_build_un_op(irb, scope, node, op_id, value);
}
static IrInstSrc *ir_gen_prefix_op_id(IrBuilderSrc *irb, Scope *scope, AstNode *node, IrUnOp op_id) {
return ir_gen_prefix_op_id_lval(irb, scope, node, op_id, LValNone);
}
static IrInstSrc *ir_expr_wrap(IrBuilderSrc *irb, Scope *scope, IrInstSrc *inst, ResultLoc *result_loc) {
if (inst == irb->codegen->invalid_inst_src) return inst;
ir_build_end_expr(irb, scope, inst->base.source_node, inst, result_loc);
return inst;
}
static IrInstSrc *ir_lval_wrap(IrBuilderSrc *irb, Scope *scope, IrInstSrc *value, LVal lval,
ResultLoc *result_loc)
{
// This logic must be kept in sync with
// [STMT_EXPR_TEST_THING] <--- (search this token)
if (value == irb->codegen->invalid_inst_src ||
instr_is_unreachable(value) ||
value->base.source_node->type == NodeTypeDefer ||
value->id == IrInstSrcIdDeclVar)
{
return value;
}
assert(lval != LValAssign);
if (lval == LValPtr) {
// We needed a pointer to a value, but we got a value. So we create
// an instruction which just makes a pointer of it.
return ir_build_ref_src(irb, scope, value->base.source_node, value);
} else if (result_loc != nullptr) {
return ir_expr_wrap(irb, scope, value, result_loc);
} else {
return value;
}
}
static PtrLen star_token_to_ptr_len(TokenId token_id) {
switch (token_id) {
case TokenIdStar:
case TokenIdStarStar:
return PtrLenSingle;
case TokenIdLBracket:
return PtrLenUnknown;
case TokenIdIdentifier:
return PtrLenC;
default:
zig_unreachable();
}
}
static Error token_number_literal_u32(IrBuilderSrc *irb, AstNode *source_node,
RootStruct *root_struct, uint32_t *result, TokenIndex token)
{
BigInt bigint;
token_number_literal_bigint(root_struct, &bigint, token);
if (!bigint_fits_in_bits(&bigint, 32, false)) {
Buf *val_buf = buf_alloc();
bigint_append_buf(val_buf, &bigint, 10);
exec_add_error_node(irb->codegen, irb->exec, source_node,
buf_sprintf("value %s too large for u32", buf_ptr(val_buf)));
bigint_deinit(&bigint);
return ErrorSemanticAnalyzeFail;
}
*result = bigint_as_u32(&bigint);
bigint_deinit(&bigint);
return ErrorNone;
}
static IrInstSrc *ir_gen_pointer_type(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
Error err;
assert(node->type == NodeTypePointerType);
RootStruct *root_struct = node->owner->data.structure.root_struct;
TokenId star_tok_id = root_struct->token_ids[node->data.pointer_type.star_token];
PtrLen ptr_len = star_token_to_ptr_len(star_tok_id);
bool is_const = node->data.pointer_type.is_const;
bool is_volatile = node->data.pointer_type.is_volatile;
bool is_allow_zero = node->data.pointer_type.allow_zero_token != 0;
AstNode *sentinel_expr = node->data.pointer_type.sentinel;
AstNode *expr_node = node->data.pointer_type.op_expr;
AstNode *align_expr = node->data.pointer_type.align_expr;
IrInstSrc *sentinel;
if (sentinel_expr != nullptr) {
sentinel = ir_gen_node(irb, sentinel_expr, scope);
if (sentinel == irb->codegen->invalid_inst_src)
return sentinel;
} else {
sentinel = nullptr;
}
IrInstSrc *align_value;
if (align_expr != nullptr) {
align_value = ir_gen_node(irb, align_expr, scope);
if (align_value == irb->codegen->invalid_inst_src)
return align_value;
} else {
align_value = nullptr;
}
IrInstSrc *child_type = ir_gen_node(irb, expr_node, scope);
if (child_type == irb->codegen->invalid_inst_src)
return child_type;
uint32_t bit_offset_start = 0;
if (node->data.pointer_type.bit_offset_start != 0) {
if ((err = token_number_literal_u32(irb, node, root_struct, &bit_offset_start,
node->data.pointer_type.bit_offset_start)))
{
return irb->codegen->invalid_inst_src;
}
}
uint32_t host_int_bytes = 0;
if (node->data.pointer_type.host_int_bytes != 0) {
if ((err = token_number_literal_u32(irb, node, root_struct, &host_int_bytes,
node->data.pointer_type.host_int_bytes)))
{
return irb->codegen->invalid_inst_src;
}
}
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_inst_src;
}
return ir_build_ptr_type(irb, scope, node, child_type, is_const, is_volatile,
ptr_len, sentinel, align_value, bit_offset_start, host_int_bytes, is_allow_zero);
}
static IrInstSrc *ir_gen_catch_unreachable(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
AstNode *expr_node, LVal lval, ResultLoc *result_loc)
{
IrInstSrc *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr);
if (err_union_ptr == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
IrInstSrc *payload_ptr = ir_build_unwrap_err_payload_src(irb, scope, source_node, err_union_ptr, true, false);
if (payload_ptr == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
if (lval == LValPtr)
return payload_ptr;
IrInstSrc *load_ptr = ir_build_load_ptr(irb, scope, source_node, payload_ptr);
return ir_expr_wrap(irb, scope, load_ptr, result_loc);
}
static IrInstSrc *ir_gen_bool_not(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypePrefixOpExpr);
AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
IrInstSrc *value = ir_gen_node(irb, expr_node, scope);
if (value == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
return ir_build_bool_not(irb, scope, node, value);
}
static IrInstSrc *ir_gen_prefix_op_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
ResultLoc *result_loc)
{
assert(node->type == NodeTypePrefixOpExpr);
PrefixOp prefix_op = node->data.prefix_op_expr.prefix_op;
switch (prefix_op) {
case PrefixOpInvalid:
zig_unreachable();
case PrefixOpBoolNot:
return ir_lval_wrap(irb, scope, ir_gen_bool_not(irb, scope, node), lval, result_loc);
case PrefixOpBinNot:
return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpBinNot), lval, result_loc);
case PrefixOpNegation:
return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegation), lval, result_loc);
case PrefixOpNegationWrap:
return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegationWrap), lval, result_loc);
case PrefixOpOptional:
return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpOptional), lval, result_loc);
case PrefixOpAddrOf: {
AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
return ir_lval_wrap(irb, scope, ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr), lval, result_loc);
}
}
zig_unreachable();
}
static IrInstSrc *ir_gen_union_init_expr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *union_type, IrInstSrc *field_name, AstNode *expr_node,
LVal lval, ResultLoc *parent_result_loc)
{
IrInstSrc *container_ptr = ir_build_resolve_result(irb, scope, source_node, parent_result_loc, union_type);
IrInstSrc *field_ptr = ir_build_field_ptr_instruction(irb, scope, source_node, container_ptr,
field_name, true);
ResultLocInstruction *result_loc_inst = heap::c_allocator.create<ResultLocInstruction>();
result_loc_inst->base.id = ResultLocIdInstruction;
result_loc_inst->base.source_instruction = field_ptr;
ir_ref_instruction(field_ptr, irb->current_basic_block);
ir_build_reset_result(irb, scope, expr_node, &result_loc_inst->base);
IrInstSrc *expr_value = ir_gen_node_extra(irb, expr_node, scope, LValNone,
&result_loc_inst->base);
if (expr_value == irb->codegen->invalid_inst_src)
return expr_value;
IrInstSrc *init_union = ir_build_union_init_named_field(irb, scope, source_node, union_type,
field_name, field_ptr, container_ptr);
return ir_lval_wrap(irb, scope, init_union, lval, parent_result_loc);
}
static IrInstSrc *ir_gen_container_init_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
ResultLoc *parent_result_loc)
{
assert(node->type == NodeTypeContainerInitExpr);
AstNodeContainerInitExpr *container_init_expr = &node->data.container_init_expr;
ContainerInitKind kind = container_init_expr->kind;
ResultLocCast *result_loc_cast = nullptr;
ResultLoc *child_result_loc;
AstNode *init_array_type_source_node;
if (container_init_expr->type != nullptr) {
IrInstSrc *container_type;
if (container_init_expr->type->type == NodeTypeInferredArrayType) {
if (kind == ContainerInitKindStruct) {
add_node_error(irb->codegen, container_init_expr->type,
buf_sprintf("initializing array with struct syntax"));
return irb->codegen->invalid_inst_src;
}
IrInstSrc *sentinel;
if (container_init_expr->type->data.inferred_array_type.sentinel != nullptr) {
sentinel = ir_gen_node(irb, container_init_expr->type->data.inferred_array_type.sentinel, scope);
if (sentinel == irb->codegen->invalid_inst_src)
return sentinel;
} else {
sentinel = nullptr;
}
IrInstSrc *elem_type = ir_gen_node(irb,
container_init_expr->type->data.inferred_array_type.child_type, scope);
if (elem_type == irb->codegen->invalid_inst_src)
return elem_type;
size_t item_count = container_init_expr->entries.length;
IrInstSrc *item_count_inst = ir_build_const_usize(irb, scope, node, item_count);
container_type = ir_build_array_type(irb, scope, node, item_count_inst, sentinel, elem_type);
} else {
container_type = ir_gen_node(irb, container_init_expr->type, scope);
if (container_type == irb->codegen->invalid_inst_src)
return container_type;
}
result_loc_cast = ir_build_cast_result_loc(irb, container_type, parent_result_loc);
child_result_loc = &result_loc_cast->base;
init_array_type_source_node = container_type->base.source_node;
} else {
child_result_loc = parent_result_loc;
if (parent_result_loc->source_instruction != nullptr) {
init_array_type_source_node = parent_result_loc->source_instruction->base.source_node;
} else {
init_array_type_source_node = node;
}
}
switch (kind) {
case ContainerInitKindStruct: {
IrInstSrc *container_ptr = ir_build_resolve_result(irb, scope, node, child_result_loc,
nullptr);
size_t field_count = container_init_expr->entries.length;
IrInstSrcContainerInitFieldsField *fields = heap::c_allocator.allocate<IrInstSrcContainerInitFieldsField>(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;
IrInstSrc *field_ptr = ir_build_field_ptr(irb, scope, entry_node, container_ptr, name, true);
ResultLocInstruction *result_loc_inst = heap::c_allocator.create<ResultLocInstruction>();
result_loc_inst->base.id = ResultLocIdInstruction;
result_loc_inst->base.source_instruction = field_ptr;
result_loc_inst->base.allow_write_through_const = true;
ir_ref_instruction(field_ptr, irb->current_basic_block);
ir_build_reset_result(irb, scope, expr_node, &result_loc_inst->base);
IrInstSrc *expr_value = ir_gen_node_extra(irb, expr_node, scope, LValNone,
&result_loc_inst->base);
if (expr_value == irb->codegen->invalid_inst_src)
return expr_value;
fields[i].name = name;
fields[i].source_node = entry_node;
fields[i].result_loc = field_ptr;
}
IrInstSrc *result = ir_build_container_init_fields(irb, scope, node, field_count,
fields, container_ptr);
if (result_loc_cast != nullptr) {
result = ir_build_implicit_cast(irb, scope, node, result, result_loc_cast);
}
return ir_lval_wrap(irb, scope, result, lval, parent_result_loc);
}
case ContainerInitKindArray: {
size_t item_count = container_init_expr->entries.length;
IrInstSrc *container_ptr = ir_build_resolve_result(irb, scope, node, child_result_loc,
nullptr);
IrInstSrc **result_locs = heap::c_allocator.allocate<IrInstSrc *>(item_count);
for (size_t i = 0; i < item_count; i += 1) {
AstNode *expr_node = container_init_expr->entries.at(i);
IrInstSrc *elem_index = ir_build_const_usize(irb, scope, expr_node, i);
IrInstSrc *elem_ptr = ir_build_elem_ptr(irb, scope, expr_node, container_ptr,
elem_index, false, PtrLenSingle, init_array_type_source_node);
ResultLocInstruction *result_loc_inst = heap::c_allocator.create<ResultLocInstruction>();
result_loc_inst->base.id = ResultLocIdInstruction;
result_loc_inst->base.source_instruction = elem_ptr;
result_loc_inst->base.allow_write_through_const = true;
ir_ref_instruction(elem_ptr, irb->current_basic_block);
ir_build_reset_result(irb, scope, expr_node, &result_loc_inst->base);
IrInstSrc *expr_value = ir_gen_node_extra(irb, expr_node, scope, LValNone,
&result_loc_inst->base);
if (expr_value == irb->codegen->invalid_inst_src)
return expr_value;
result_locs[i] = elem_ptr;
}
IrInstSrc *result = ir_build_container_init_list(irb, scope, node, item_count,
result_locs, container_ptr, init_array_type_source_node);
if (result_loc_cast != nullptr) {
result = ir_build_implicit_cast(irb, scope, node, result, result_loc_cast);
}
return ir_lval_wrap(irb, scope, result, lval, parent_result_loc);
}
}
zig_unreachable();
}
static ResultLocVar *ir_build_var_result_loc(IrBuilderSrc *irb, IrInstSrc *alloca, ZigVar *var) {
ResultLocVar *result_loc_var = heap::c_allocator.create<ResultLocVar>();
result_loc_var->base.id = ResultLocIdVar;
result_loc_var->base.source_instruction = alloca;
result_loc_var->base.allow_write_through_const = true;
result_loc_var->var = var;
ir_build_reset_result(irb, alloca->base.scope, alloca->base.source_node, &result_loc_var->base);
return result_loc_var;
}
static ResultLocCast *ir_build_cast_result_loc(IrBuilderSrc *irb, IrInstSrc *dest_type,
ResultLoc *parent_result_loc)
{
ResultLocCast *result_loc_cast = heap::c_allocator.create<ResultLocCast>();
result_loc_cast->base.id = ResultLocIdCast;
result_loc_cast->base.source_instruction = dest_type;
result_loc_cast->base.allow_write_through_const = parent_result_loc->allow_write_through_const;
ir_ref_instruction(dest_type, irb->current_basic_block);
result_loc_cast->parent = parent_result_loc;
ir_build_reset_result(irb, dest_type->base.scope, dest_type->base.source_node, &result_loc_cast->base);
return result_loc_cast;
}
static void build_decl_var_and_init(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigVar *var,
IrInstSrc *init, const char *name_hint, IrInstSrc *is_comptime)
{
IrInstSrc *alloca = ir_build_alloca_src(irb, scope, source_node, nullptr, name_hint, is_comptime);
ResultLocVar *var_result_loc = ir_build_var_result_loc(irb, alloca, var);
ir_build_end_expr(irb, scope, source_node, init, &var_result_loc->base);
ir_build_var_decl_src(irb, scope, source_node, var, nullptr, alloca);
}
static IrInstSrc *ir_gen_var_decl(IrBuilderSrc *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_inst_src;
}
// Used for the type expr and the align expr
Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope);
IrInstSrc *type_instruction;
if (variable_declaration->type != nullptr) {
type_instruction = ir_gen_node(irb, variable_declaration->type, comptime_scope);
if (type_instruction == irb->codegen->invalid_inst_src)
return type_instruction;
} else {
type_instruction = nullptr;
}
bool is_shadowable = false;
bool is_const = variable_declaration->is_const;
bool is_extern = variable_declaration->is_extern;
bool is_comptime_scalar = ir_should_inline(irb->exec, scope) || variable_declaration->is_comptime;
IrInstSrc *is_comptime = ir_build_const_bool(irb, scope, node, is_comptime_scalar);
ZigVar *var = ir_create_var(irb, node, scope, variable_declaration->symbol,
is_const, is_const, is_shadowable, is_comptime);
// we detect IrInstSrcDeclVar 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_inst_src;
}
IrInstSrc *align_value = nullptr;
if (variable_declaration->align_expr != nullptr) {
align_value = ir_gen_node(irb, variable_declaration->align_expr, comptime_scope);
if (align_value == irb->codegen->invalid_inst_src)
return align_value;
}
if (variable_declaration->section_expr != nullptr) {
add_node_error(irb->codegen, variable_declaration->section_expr,
buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol)));
}
// Parser should ensure that this never happens
assert(variable_declaration->threadlocal_tok == 0);
IrInstSrc *alloca = ir_build_alloca_src(irb, scope, node, align_value,
buf_ptr(variable_declaration->symbol), is_comptime);
// Create a result location for the initialization expression.
ResultLocVar *result_loc_var = ir_build_var_result_loc(irb, alloca, var);
ResultLoc *init_result_loc;
ResultLocCast *result_loc_cast;
if (type_instruction != nullptr) {
result_loc_cast = ir_build_cast_result_loc(irb, type_instruction, &result_loc_var->base);
init_result_loc = &result_loc_cast->base;
} else {
result_loc_cast = nullptr;
init_result_loc = &result_loc_var->base;
}
Scope *init_scope = is_comptime_scalar ?
create_comptime_scope(irb->codegen, variable_declaration->expr, scope) : scope;
// Temporarily set the name of the Stage1Zir 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;
IrInstSrc *init_value = ir_gen_node_extra(irb, variable_declaration->expr, init_scope,
LValNone, init_result_loc);
irb->exec->name = old_exec_name;
if (init_value == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
if (result_loc_cast != nullptr) {
IrInstSrc *implicit_cast = ir_build_implicit_cast(irb, scope, init_value->base.source_node,
init_value, result_loc_cast);
ir_build_end_expr(irb, scope, node, implicit_cast, &result_loc_var->base);
}
return ir_build_var_decl_src(irb, scope, node, var, align_value, alloca);
}
static IrInstSrc *ir_gen_while_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
ResultLoc *result_loc)
{
assert(node->type == NodeTypeWhileExpr);
AstNode *continue_expr_node = node->data.while_expr.continue_expr;
AstNode *else_node = node->data.while_expr.else_node;
IrBasicBlockSrc *cond_block = ir_create_basic_block(irb, scope, "WhileCond");
IrBasicBlockSrc *body_block = ir_create_basic_block(irb, scope, "WhileBody");
IrBasicBlockSrc *continue_block = continue_expr_node ?
ir_create_basic_block(irb, scope, "WhileContinue") : cond_block;
IrBasicBlockSrc *end_block = ir_create_basic_block(irb, scope, "WhileEnd");
IrBasicBlockSrc *else_block = else_node ?
ir_create_basic_block(irb, scope, "WhileElse") : end_block;
IrInstSrc *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;
}
ScopeExpr *spill_scope = create_expr_scope(irb->codegen, node, payload_scope);
IrInstSrc *err_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, subexpr_scope,
LValPtr, nullptr);
if (err_val_ptr == irb->codegen->invalid_inst_src)
return err_val_ptr;
IrInstSrc *is_err = ir_build_test_err_src(irb, scope, node->data.while_expr.condition, err_val_ptr,
true, false);
IrBasicBlockSrc *after_cond_block = irb->current_basic_block;
IrInstSrc *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node));
IrInstSrc *cond_br_inst;
if (!instr_is_unreachable(is_err)) {
cond_br_inst = ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_err,
else_block, body_block, is_comptime);
cond_br_inst->is_gen = true;
} else {
// for the purposes of the source instruction to ir_build_result_peers
cond_br_inst = irb->current_basic_block->instruction_list.last();
}
ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, result_loc,
is_comptime);
ir_set_cursor_at_end_and_append_block(irb, body_block);
if (var_symbol) {
IrInstSrc *payload_ptr = ir_build_unwrap_err_payload_src(irb, &spill_scope->base, symbol_node,
err_val_ptr, false, false);
IrInstSrc *var_value = node->data.while_expr.var_is_ptr ?
payload_ptr : ir_build_load_ptr(irb, &spill_scope->base, symbol_node, payload_ptr);
build_decl_var_and_init(irb, payload_scope, symbol_node, payload_var, var_value, buf_ptr(var_symbol), is_comptime);
}
ZigList<IrInstSrc *> incoming_values = {0};
ZigList<IrBasicBlockSrc *> incoming_blocks = {0};
if (is_duplicate_label(irb->codegen, payload_scope, node, node->data.while_expr.name))
return irb->codegen->invalid_inst_src;
ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, payload_scope);
loop_scope->break_block = end_block;
loop_scope->continue_block = continue_block;
loop_scope->is_comptime = is_comptime;
loop_scope->incoming_blocks = &incoming_blocks;
loop_scope->incoming_values = &incoming_values;
loop_scope->lval = lval;
loop_scope->peer_parent = peer_parent;
loop_scope->spill_scope = spill_scope;
// Note the body block of the loop is not the place that lval and result_loc are used -
// it's actually in break statements, handled similarly to return statements.
// That is why we set those values in loop_scope above and not in this ir_gen_node call.
IrInstSrc *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base);
if (body_result == irb->codegen->invalid_inst_src)
return body_result;
if (loop_scope->name != nullptr && loop_scope->name_used == false) {
add_node_error(irb->codegen, node, buf_sprintf("unused while label"));
}
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);
IrInstSrc *expr_result = ir_gen_node(irb, continue_expr_node, payload_scope);
if (expr_result == irb->codegen->invalid_inst_src)
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;
IrInstSrc *err_ptr = ir_build_unwrap_err_code_src(irb, err_scope, err_symbol_node, err_val_ptr);
IrInstSrc *err_value = ir_build_load_ptr(irb, err_scope, err_symbol_node, err_ptr);
build_decl_var_and_init(irb, err_scope, err_symbol_node, err_var, err_value, buf_ptr(err_symbol), is_comptime);
if (peer_parent->peers.length != 0) {
peer_parent->peers.last()->next_bb = else_block;
}
ResultLocPeer *peer_result = create_peer_result(peer_parent);
peer_parent->peers.append(peer_result);
IrInstSrc *else_result = ir_gen_node_extra(irb, else_node, err_scope, lval, &peer_result->base);
if (else_result == irb->codegen->invalid_inst_src)
return else_result;
if (!instr_is_unreachable(else_result))
ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime));
IrBasicBlockSrc *after_else_block = irb->current_basic_block;
ir_set_cursor_at_end_and_append_block(irb, end_block);
if (else_result) {
incoming_blocks.append(after_else_block);
incoming_values.append(else_result);
} else {
incoming_blocks.append(after_cond_block);
incoming_values.append(void_else_result);
}
if (peer_parent->peers.length != 0) {
peer_parent->peers.last()->next_bb = end_block;
}
IrInstSrc *phi = ir_build_phi(irb, scope, node, incoming_blocks.length,
incoming_blocks.items, incoming_values.items, peer_parent);
return ir_expr_wrap(irb, scope, phi, result_loc);
} else if (var_symbol != nullptr) {
ir_set_cursor_at_end_and_append_block(irb, cond_block);
Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
// TODO make it an error to write to payload variable
AstNode *symbol_node = node; // TODO make more accurate
ZigVar *payload_var = ir_create_var(irb, symbol_node, subexpr_scope, var_symbol,
true, false, false, is_comptime);
Scope *child_scope = payload_var->child_scope;
ScopeExpr *spill_scope = create_expr_scope(irb->codegen, node, child_scope);
IrInstSrc *maybe_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, subexpr_scope,
LValPtr, nullptr);
if (maybe_val_ptr == irb->codegen->invalid_inst_src)
return maybe_val_ptr;
IrInstSrc *maybe_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, maybe_val_ptr);
IrInstSrc *is_non_null = ir_build_test_non_null_src(irb, scope, node->data.while_expr.condition, maybe_val);
IrBasicBlockSrc *after_cond_block = irb->current_basic_block;
IrInstSrc *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node));
IrInstSrc *cond_br_inst;
if (!instr_is_unreachable(is_non_null)) {
cond_br_inst = ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_non_null,
body_block, else_block, is_comptime);
cond_br_inst->is_gen = true;
} else {
// for the purposes of the source instruction to ir_build_result_peers
cond_br_inst = irb->current_basic_block->instruction_list.last();
}
ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, result_loc,
is_comptime);
ir_set_cursor_at_end_and_append_block(irb, body_block);
IrInstSrc *payload_ptr = ir_build_optional_unwrap_ptr(irb, &spill_scope->base, symbol_node, maybe_val_ptr, false);
IrInstSrc *var_value = node->data.while_expr.var_is_ptr ?
payload_ptr : ir_build_load_ptr(irb, &spill_scope->base, symbol_node, payload_ptr);
build_decl_var_and_init(irb, child_scope, symbol_node, payload_var, var_value, buf_ptr(var_symbol), is_comptime);
ZigList<IrInstSrc *> incoming_values = {0};
ZigList<IrBasicBlockSrc *> incoming_blocks = {0};
if (is_duplicate_label(irb->codegen, child_scope, node, node->data.while_expr.name))
return irb->codegen->invalid_inst_src;
ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, child_scope);
loop_scope->break_block = end_block;
loop_scope->continue_block = continue_block;
loop_scope->is_comptime = is_comptime;
loop_scope->incoming_blocks = &incoming_blocks;
loop_scope->incoming_values = &incoming_values;
loop_scope->lval = lval;
loop_scope->peer_parent = peer_parent;
loop_scope->spill_scope = spill_scope;
// Note the body block of the loop is not the place that lval and result_loc are used -
// it's actually in break statements, handled similarly to return statements.
// That is why we set those values in loop_scope above and not in this ir_gen_node call.
IrInstSrc *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base);
if (body_result == irb->codegen->invalid_inst_src)
return body_result;
if (loop_scope->name != nullptr && loop_scope->name_used == false) {
add_node_error(irb->codegen, node, buf_sprintf("unused while label"));
}
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);
IrInstSrc *expr_result = ir_gen_node(irb, continue_expr_node, child_scope);
if (expr_result == irb->codegen->invalid_inst_src)
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));
}
}
IrInstSrc *else_result = nullptr;
if (else_node) {
ir_set_cursor_at_end_and_append_block(irb, else_block);
if (peer_parent->peers.length != 0) {
peer_parent->peers.last()->next_bb = else_block;
}
ResultLocPeer *peer_result = create_peer_result(peer_parent);
peer_parent->peers.append(peer_result);
else_result = ir_gen_node_extra(irb, else_node, scope, lval, &peer_result->base);
if (else_result == irb->codegen->invalid_inst_src)
return else_result;
if (!instr_is_unreachable(else_result))
ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime));
}
IrBasicBlockSrc *after_else_block = irb->current_basic_block;
ir_set_cursor_at_end_and_append_block(irb, end_block);
if (else_result) {
incoming_blocks.append(after_else_block);
incoming_values.append(else_result);
} else {
incoming_blocks.append(after_cond_block);
incoming_values.append(void_else_result);
}
if (peer_parent->peers.length != 0) {
peer_parent->peers.last()->next_bb = end_block;
}
IrInstSrc *phi = ir_build_phi(irb, scope, node, incoming_blocks.length,
incoming_blocks.items, incoming_values.items, peer_parent);
return ir_expr_wrap(irb, scope, phi, result_loc);
} else {
ir_set_cursor_at_end_and_append_block(irb, cond_block);
IrInstSrc *cond_val = ir_gen_node(irb, node->data.while_expr.condition, scope);
if (cond_val == irb->codegen->invalid_inst_src)
return cond_val;
IrBasicBlockSrc *after_cond_block = irb->current_basic_block;
IrInstSrc *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node));
IrInstSrc *cond_br_inst;
if (!instr_is_unreachable(cond_val)) {
cond_br_inst = ir_build_cond_br(irb, scope, node->data.while_expr.condition, cond_val,
body_block, else_block, is_comptime);
cond_br_inst->is_gen = true;
} else {
// for the purposes of the source instruction to ir_build_result_peers
cond_br_inst = irb->current_basic_block->instruction_list.last();
}
ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, result_loc,
is_comptime);
ir_set_cursor_at_end_and_append_block(irb, body_block);
ZigList<IrInstSrc *> incoming_values = {0};
ZigList<IrBasicBlockSrc *> incoming_blocks = {0};
Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
if (is_duplicate_label(irb->codegen, subexpr_scope, node, node->data.while_expr.name))
return irb->codegen->invalid_inst_src;
ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, subexpr_scope);
loop_scope->break_block = end_block;
loop_scope->continue_block = continue_block;
loop_scope->is_comptime = is_comptime;
loop_scope->incoming_blocks = &incoming_blocks;
loop_scope->incoming_values = &incoming_values;
loop_scope->lval = lval;
loop_scope->peer_parent = peer_parent;
// Note the body block of the loop is not the place that lval and result_loc are used -
// it's actually in break statements, handled similarly to return statements.
// That is why we set those values in loop_scope above and not in this ir_gen_node call.
IrInstSrc *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base);
if (body_result == irb->codegen->invalid_inst_src)
return body_result;
if (loop_scope->name != nullptr && loop_scope->name_used == false) {
add_node_error(irb->codegen, node, buf_sprintf("unused while label"));
}
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);
IrInstSrc *expr_result = ir_gen_node(irb, continue_expr_node, subexpr_scope);
if (expr_result == irb->codegen->invalid_inst_src)
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));
}
}
IrInstSrc *else_result = nullptr;
if (else_node) {
ir_set_cursor_at_end_and_append_block(irb, else_block);
if (peer_parent->peers.length != 0) {
peer_parent->peers.last()->next_bb = else_block;
}
ResultLocPeer *peer_result = create_peer_result(peer_parent);
peer_parent->peers.append(peer_result);
else_result = ir_gen_node_extra(irb, else_node, subexpr_scope, lval, &peer_result->base);
if (else_result == irb->codegen->invalid_inst_src)
return else_result;
if (!instr_is_unreachable(else_result))
ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime));
}
IrBasicBlockSrc *after_else_block = irb->current_basic_block;
ir_set_cursor_at_end_and_append_block(irb, end_block);
if (else_result) {
incoming_blocks.append(after_else_block);
incoming_values.append(else_result);
} else {
incoming_blocks.append(after_cond_block);
incoming_values.append(void_else_result);
}
if (peer_parent->peers.length != 0) {
peer_parent->peers.last()->next_bb = end_block;
}
IrInstSrc *phi = ir_build_phi(irb, scope, node, incoming_blocks.length,
incoming_blocks.items, incoming_values.items, peer_parent);
return ir_expr_wrap(irb, scope, phi, result_loc);
}
}
static IrInstSrc *ir_gen_for_expr(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node, LVal lval,
ResultLoc *result_loc)
{
assert(node->type == NodeTypeForExpr);
AstNode *array_node = node->data.for_expr.array_expr;
AstNode *elem_node = node->data.for_expr.elem_node;
AstNode *index_node = node->data.for_expr.index_node;
AstNode *body_node = node->data.for_expr.body;
AstNode *else_node = node->data.for_expr.else_node;
if (!elem_node) {
add_node_error(irb->codegen, node, buf_sprintf("for loop expression missing element parameter"));
return irb->codegen->invalid_inst_src;
}
assert(elem_node->type == NodeTypeIdentifier);
ScopeExpr *spill_scope = create_expr_scope(irb->codegen, node, parent_scope);
IrInstSrc *array_val_ptr = ir_gen_node_extra(irb, array_node, &spill_scope->base, LValPtr, nullptr);
if (array_val_ptr == irb->codegen->invalid_inst_src)
return array_val_ptr;
IrInstSrc *is_comptime = ir_build_const_bool(irb, parent_scope, node,
ir_should_inline(irb->exec, parent_scope) || node->data.for_expr.is_inline);
AstNode *index_var_source_node;
ZigVar *index_var;
const char *index_var_name;
if (index_node) {
index_var_source_node = index_node;
Buf *index_var_name_buf = node_identifier_buf(index_node);
index_var = ir_create_var(irb, index_node, parent_scope, index_var_name_buf, true, false, false, is_comptime);
index_var_name = buf_ptr(index_var_name_buf);
} else {
index_var_source_node = node;
index_var = ir_create_var(irb, node, parent_scope, nullptr, true, false, true, is_comptime);
index_var_name = "i";
}
IrInstSrc *zero = ir_build_const_usize(irb, parent_scope, node, 0);
build_decl_var_and_init(irb, parent_scope, index_var_source_node, index_var, zero, index_var_name, is_comptime);
parent_scope = index_var->child_scope;
IrInstSrc *one = ir_build_const_usize(irb, parent_scope, node, 1);
IrInstSrc *index_ptr = ir_build_var_ptr(irb, parent_scope, node, index_var);
IrBasicBlockSrc *cond_block = ir_create_basic_block(irb, parent_scope, "ForCond");
IrBasicBlockSrc *body_block = ir_create_basic_block(irb, parent_scope, "ForBody");
IrBasicBlockSrc *end_block = ir_create_basic_block(irb, parent_scope, "ForEnd");
IrBasicBlockSrc *else_block = else_node ? ir_create_basic_block(irb, parent_scope, "ForElse") : end_block;
IrBasicBlockSrc *continue_block = ir_create_basic_block(irb, parent_scope, "ForContinue");
Buf *len_field_name = buf_create_from_str("len");
IrInstSrc *len_ref = ir_build_field_ptr(irb, parent_scope, node, array_val_ptr, len_field_name, false);
IrInstSrc *len_val = ir_build_load_ptr(irb, &spill_scope->base, node, len_ref);
ir_build_br(irb, parent_scope, node, cond_block, is_comptime);
ir_set_cursor_at_end_and_append_block(irb, cond_block);
IrInstSrc *index_val = ir_build_load_ptr(irb, &spill_scope->base, node, index_ptr);
IrInstSrc *cond = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpLessThan, index_val, len_val, false);
IrBasicBlockSrc *after_cond_block = irb->current_basic_block;
IrInstSrc *void_else_value = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, parent_scope, node));
IrInstSrc *cond_br_inst = ir_mark_gen(ir_build_cond_br(irb, parent_scope, node, cond,
body_block, else_block, is_comptime));
ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, result_loc, is_comptime);
ir_set_cursor_at_end_and_append_block(irb, body_block);
IrInstSrc *elem_ptr = ir_build_elem_ptr(irb, &spill_scope->base, node, array_val_ptr, index_val,
false, PtrLenSingle, nullptr);
// TODO make it an error to write to element variable or i variable.
Buf *elem_var_name = node_identifier_buf(elem_node);
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;
IrInstSrc *elem_value = node->data.for_expr.elem_is_ptr ?
elem_ptr : ir_build_load_ptr(irb, &spill_scope->base, elem_node, elem_ptr);
build_decl_var_and_init(irb, parent_scope, elem_node, elem_var, elem_value, buf_ptr(elem_var_name), is_comptime);
if (is_duplicate_label(irb->codegen, child_scope, node, node->data.for_expr.name))
return irb->codegen->invalid_inst_src;
ZigList<IrInstSrc *> incoming_values = {0};
ZigList<IrBasicBlockSrc *> incoming_blocks = {0};
ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, child_scope);
loop_scope->break_block = end_block;
loop_scope->continue_block = continue_block;
loop_scope->is_comptime = is_comptime;
loop_scope->incoming_blocks = &incoming_blocks;
loop_scope->incoming_values = &incoming_values;
loop_scope->lval = LValNone;
loop_scope->peer_parent = peer_parent;
loop_scope->spill_scope = spill_scope;
// Note the body block of the loop is not the place that lval and result_loc are used -
// it's actually in break statements, handled similarly to return statements.
// That is why we set those values in loop_scope above and not in this ir_gen_node call.
IrInstSrc *body_result = ir_gen_node(irb, body_node, &loop_scope->base);
if (body_result == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
if (loop_scope->name != nullptr && loop_scope->name_used == false) {
add_node_error(irb->codegen, node, buf_sprintf("unused for label"));
}
if (!instr_is_unreachable(body_result)) {
ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.for_expr.body, body_result));
ir_mark_gen(ir_build_br(irb, child_scope, node, continue_block, is_comptime));
}
ir_set_cursor_at_end_and_append_block(irb, continue_block);
IrInstSrc *new_index_val = ir_build_bin_op(irb, child_scope, node, IrBinOpAdd, index_val, one, false);
ir_build_store_ptr(irb, child_scope, node, index_ptr, new_index_val)->allow_write_through_const = true;
ir_build_br(irb, child_scope, node, cond_block, is_comptime);
IrInstSrc *else_result = nullptr;
if (else_node) {
ir_set_cursor_at_end_and_append_block(irb, else_block);
if (peer_parent->peers.length != 0) {
peer_parent->peers.last()->next_bb = else_block;
}
ResultLocPeer *peer_result = create_peer_result(peer_parent);
peer_parent->peers.append(peer_result);
else_result = ir_gen_node_extra(irb, else_node, parent_scope, LValNone, &peer_result->base);
if (else_result == irb->codegen->invalid_inst_src)
return else_result;
if (!instr_is_unreachable(else_result))
ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime));
}
IrBasicBlockSrc *after_else_block = irb->current_basic_block;
ir_set_cursor_at_end_and_append_block(irb, end_block);
if (else_result) {
incoming_blocks.append(after_else_block);
incoming_values.append(else_result);
} else {
incoming_blocks.append(after_cond_block);
incoming_values.append(void_else_value);
}
if (peer_parent->peers.length != 0) {
peer_parent->peers.last()->next_bb = end_block;
}
IrInstSrc *phi = ir_build_phi(irb, parent_scope, node, incoming_blocks.length,
incoming_blocks.items, incoming_values.items, peer_parent);
return ir_lval_wrap(irb, parent_scope, phi, lval, result_loc);
}
static IrInstSrc *ir_gen_bool_literal(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeBoolLiteral);
return ir_build_const_bool(irb, scope, node, node->data.bool_literal.value);
}
static IrInstSrc *ir_gen_enum_literal(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeEnumLiteral);
RootStruct *root_struct = node->owner->data.structure.root_struct;
Buf *name = token_identifier_buf(root_struct, node->main_token + 1);
return ir_build_const_enum_literal(irb, scope, node, name);
}
static IrInstSrc *ir_gen_string_literal(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
Error err;
assert(node->type == NodeTypeStringLiteral);
RootStruct *root_struct = node->owner->data.structure.root_struct;
const char *source = buf_ptr(root_struct->source_code);
TokenId *token_ids = root_struct->token_ids;
Buf *str = buf_alloc();
if (token_ids[node->main_token] == TokenIdStringLiteral) {
size_t byte_offset = root_struct->token_locs[node->main_token].offset;
size_t bad_index;
if ((err = source_string_literal_buf(source + byte_offset, str, &bad_index))) {
add_token_error_offset(irb->codegen, node->owner, node->main_token,
buf_create_from_str("invalid string literal character"), bad_index);
}
src_assert(source[byte_offset] == '"', node);
byte_offset += 1;
} else if (token_ids[node->main_token] == TokenIdMultilineStringLiteralLine) {
TokenIndex tok_index = node->main_token;
bool first = true;
for (;token_ids[tok_index] == TokenIdMultilineStringLiteralLine; tok_index += 1) {
size_t byte_offset = root_struct->token_locs[tok_index].offset;
size_t end = byte_offset;
while (source[end] != 0 && source[end] != '\n') {
end += 1;
}
if (!first) {
buf_append_char(str, '\n');
} else {
first = false;
}
buf_append_mem(str, source + byte_offset + 2, end - byte_offset - 2);
}
} else {
zig_unreachable();
}
return ir_build_const_str_lit(irb, scope, node, str);
}
static IrInstSrc *ir_gen_array_type(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeArrayType);
AstNode *size_node = node->data.array_type.size;
AstNode *child_type_node = node->data.array_type.child_type;
bool is_const = node->data.array_type.is_const;
bool is_volatile = node->data.array_type.is_volatile;
bool is_allow_zero = node->data.array_type.allow_zero_token != 0;
AstNode *sentinel_expr = node->data.array_type.sentinel;
AstNode *align_expr = node->data.array_type.align_expr;
Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope);
IrInstSrc *sentinel;
if (sentinel_expr != nullptr) {
sentinel = ir_gen_node(irb, sentinel_expr, comptime_scope);
if (sentinel == irb->codegen->invalid_inst_src)
return sentinel;
} else {
sentinel = nullptr;
}
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_inst_src;
}
if (is_volatile) {
add_node_error(irb->codegen, node, buf_create_from_str("volatile qualifier invalid on array type"));
return irb->codegen->invalid_inst_src;
}
if (is_allow_zero) {
add_node_error(irb->codegen, node, buf_create_from_str("allowzero qualifier invalid on array type"));
return irb->codegen->invalid_inst_src;
}
if (align_expr != nullptr) {
add_node_error(irb->codegen, node, buf_create_from_str("align qualifier invalid on array type"));
return irb->codegen->invalid_inst_src;
}
IrInstSrc *size_value = ir_gen_node(irb, size_node, comptime_scope);
if (size_value == irb->codegen->invalid_inst_src)
return size_value;
IrInstSrc *child_type = ir_gen_node(irb, child_type_node, comptime_scope);
if (child_type == irb->codegen->invalid_inst_src)
return child_type;
return ir_build_array_type(irb, scope, node, size_value, sentinel, child_type);
} else {
IrInstSrc *align_value;
if (align_expr != nullptr) {
align_value = ir_gen_node(irb, align_expr, comptime_scope);
if (align_value == irb->codegen->invalid_inst_src)
return align_value;
} else {
align_value = nullptr;
}
IrInstSrc *child_type = ir_gen_node(irb, child_type_node, comptime_scope);
if (child_type == irb->codegen->invalid_inst_src)
return child_type;
return ir_build_slice_type(irb, scope, node, child_type, is_const, is_volatile, sentinel,
align_value, is_allow_zero);
}
}
static IrInstSrc *ir_gen_anyframe_type(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeAnyFrameType);
AstNode *payload_type_node = node->data.anyframe_type.payload_type;
IrInstSrc *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_inst_src)
return payload_type_value;
}
return ir_build_anyframe_type(irb, scope, node, payload_type_value);
}
static IrInstSrc *ir_gen_undefined_literal(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeUndefinedLiteral);
return ir_build_const_undefined(irb, scope, node);
}
static IrInstSrc *ir_gen_asm_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeAsmExpr);
AstNodeAsmExpr *asm_expr = &node->data.asm_expr;
IrInstSrc *asm_template = ir_gen_node(irb, asm_expr->asm_template, scope);
if (asm_template == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
bool is_volatile = asm_expr->volatile_token != 0;
bool in_fn_scope = (scope_fn_entry(scope) != nullptr);
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_inst_src;
}
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_inst_src;
}
return ir_build_asm_src(irb, scope, node, asm_template, nullptr, nullptr,
nullptr, 0, is_volatile, true);
}
IrInstSrc **input_list = heap::c_allocator.allocate<IrInstSrc *>(asm_expr->input_list.length);
IrInstSrc **output_types = heap::c_allocator.allocate<IrInstSrc *>(asm_expr->output_list.length);
ZigVar **output_vars = heap::c_allocator.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_inst_src;
}
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;
IrInstSrc *return_type = ir_gen_node(irb, asm_output->return_type, scope);
if (return_type == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
if (return_count > 1) {
add_node_error(irb->codegen, node,
buf_sprintf("inline assembly allows up to one output value"));
return irb->codegen->invalid_inst_src;
}
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_inst_src;
}
}
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_inst_src;
}
}
for (size_t i = 0; i < asm_expr->input_list.length; i += 1) {
AsmInput *asm_input = asm_expr->input_list.at(i);
IrInstSrc *input_value = ir_gen_node(irb, asm_input->expr, scope);
if (input_value == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
input_list[i] = input_value;
}
return ir_build_asm_src(irb, scope, node, asm_template, input_list, output_types,
output_vars, return_count, is_volatile, false);
}
static IrInstSrc *ir_gen_if_optional_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
ResultLoc *result_loc)
{
assert(node->type == NodeTypeIfOptional);
Buf *var_symbol = node->data.test_expr.var_symbol;
AstNode *expr_node = node->data.test_expr.target_node;
AstNode *then_node = node->data.test_expr.then_node;
AstNode *else_node = node->data.test_expr.else_node;
bool var_is_ptr = node->data.test_expr.var_is_ptr;
ScopeExpr *spill_scope = create_expr_scope(irb->codegen, expr_node, scope);
spill_scope->spill_harder = true;
IrInstSrc *maybe_val_ptr = ir_gen_node_extra(irb, expr_node, &spill_scope->base, LValPtr, nullptr);
if (maybe_val_ptr == irb->codegen->invalid_inst_src)
return maybe_val_ptr;
IrInstSrc *maybe_val = ir_build_load_ptr(irb, scope, node, maybe_val_ptr);
IrInstSrc *is_non_null = ir_build_test_non_null_src(irb, scope, node, maybe_val);
IrBasicBlockSrc *then_block = ir_create_basic_block(irb, scope, "OptionalThen");
IrBasicBlockSrc *else_block = ir_create_basic_block(irb, scope, "OptionalElse");
IrBasicBlockSrc *endif_block = ir_create_basic_block(irb, scope, "OptionalEndIf");
IrInstSrc *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);
}
IrInstSrc *cond_br_inst = ir_build_cond_br(irb, scope, node, is_non_null,
then_block, else_block, is_comptime);
ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, else_block, endif_block,
result_loc, is_comptime);
ir_set_cursor_at_end_and_append_block(irb, then_block);
Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, &spill_scope->base, is_comptime);
Scope *var_scope;
if (var_symbol) {
bool is_shadowable = false;
bool is_const = true;
ZigVar *var = ir_create_var(irb, node, subexpr_scope,
var_symbol, is_const, is_const, is_shadowable, is_comptime);
IrInstSrc *payload_ptr = ir_build_optional_unwrap_ptr(irb, subexpr_scope, node, maybe_val_ptr, false);
IrInstSrc *var_value = var_is_ptr ?
payload_ptr : ir_build_load_ptr(irb, &spill_scope->base, node, payload_ptr);
build_decl_var_and_init(irb, subexpr_scope, node, var, var_value, buf_ptr(var_symbol), is_comptime);
var_scope = var->child_scope;
} else {
var_scope = subexpr_scope;
}
IrInstSrc *then_expr_result = ir_gen_node_extra(irb, then_node, var_scope, lval,
&peer_parent->peers.at(0)->base);
if (then_expr_result == irb->codegen->invalid_inst_src)
return then_expr_result;
IrBasicBlockSrc *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);
IrInstSrc *else_expr_result;
if (else_node) {
else_expr_result = ir_gen_node_extra(irb, else_node, subexpr_scope, lval, &peer_parent->peers.at(1)->base);
if (else_expr_result == irb->codegen->invalid_inst_src)
return else_expr_result;
} else {
else_expr_result = ir_build_const_void(irb, scope, node);
ir_build_end_expr(irb, scope, node, else_expr_result, &peer_parent->peers.at(1)->base);
}
IrBasicBlockSrc *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);
IrInstSrc **incoming_values = heap::c_allocator.allocate<IrInstSrc *>(2);
incoming_values[0] = then_expr_result;
incoming_values[1] = else_expr_result;
IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate<IrBasicBlockSrc *>(2);
incoming_blocks[0] = after_then_block;
incoming_blocks[1] = after_else_block;
IrInstSrc *phi = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, peer_parent);
return ir_expr_wrap(irb, scope, phi, result_loc);
}
static IrInstSrc *ir_gen_if_err_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
ResultLoc *result_loc)
{
assert(node->type == NodeTypeIfErrorExpr);
AstNode *target_node = node->data.if_err_expr.target_node;
AstNode *then_node = node->data.if_err_expr.then_node;
AstNode *else_node = node->data.if_err_expr.else_node;
bool var_is_ptr = node->data.if_err_expr.var_is_ptr;
bool var_is_const = true;
Buf *var_symbol = node->data.if_err_expr.var_symbol;
Buf *err_symbol = node->data.if_err_expr.err_symbol;
IrInstSrc *err_val_ptr = ir_gen_node_extra(irb, target_node, scope, LValPtr, nullptr);
if (err_val_ptr == irb->codegen->invalid_inst_src)
return err_val_ptr;
IrInstSrc *err_val = ir_build_load_ptr(irb, scope, node, err_val_ptr);
IrInstSrc *is_err = ir_build_test_err_src(irb, scope, node, err_val_ptr, true, false);
IrBasicBlockSrc *ok_block = ir_create_basic_block(irb, scope, "TryOk");
IrBasicBlockSrc *else_block = ir_create_basic_block(irb, scope, "TryElse");
IrBasicBlockSrc *endif_block = ir_create_basic_block(irb, scope, "TryEnd");
bool force_comptime = ir_should_inline(irb->exec, scope);
IrInstSrc *is_comptime = force_comptime ? ir_build_const_bool(irb, scope, node, true) : ir_build_test_comptime(irb, scope, node, is_err);
IrInstSrc *cond_br_inst = ir_build_cond_br(irb, scope, node, is_err, else_block, ok_block, is_comptime);
ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, else_block, endif_block,
result_loc, is_comptime);
ir_set_cursor_at_end_and_append_block(irb, ok_block);
Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
Scope *var_scope;
if (var_symbol) {
bool is_shadowable = false;
IrInstSrc *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);
IrInstSrc *payload_ptr = ir_build_unwrap_err_payload_src(irb, subexpr_scope, node, err_val_ptr, false, false);
IrInstSrc *var_value = var_is_ptr ?
payload_ptr : ir_build_load_ptr(irb, subexpr_scope, node, payload_ptr);
build_decl_var_and_init(irb, subexpr_scope, node, var, var_value, buf_ptr(var_symbol), var_is_comptime);
var_scope = var->child_scope;
} else {
var_scope = subexpr_scope;
}
IrInstSrc *then_expr_result = ir_gen_node_extra(irb, then_node, var_scope, lval,
&peer_parent->peers.at(0)->base);
if (then_expr_result == irb->codegen->invalid_inst_src)
return then_expr_result;
IrBasicBlockSrc *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);
IrInstSrc *else_expr_result;
if (else_node) {
Scope *err_var_scope;
if (err_symbol) {
bool is_shadowable = false;
bool is_const = true;
ZigVar *var = ir_create_var(irb, node, subexpr_scope,
err_symbol, is_const, is_const, is_shadowable, is_comptime);
IrInstSrc *err_ptr = ir_build_unwrap_err_code_src(irb, subexpr_scope, node, err_val_ptr);
IrInstSrc *err_value = ir_build_load_ptr(irb, subexpr_scope, node, err_ptr);
build_decl_var_and_init(irb, subexpr_scope, node, var, err_value, buf_ptr(err_symbol), is_comptime);
err_var_scope = var->child_scope;
} else {
err_var_scope = subexpr_scope;
}
else_expr_result = ir_gen_node_extra(irb, else_node, err_var_scope, lval, &peer_parent->peers.at(1)->base);
if (else_expr_result == irb->codegen->invalid_inst_src)
return else_expr_result;
} else {
else_expr_result = ir_build_const_void(irb, scope, node);
ir_build_end_expr(irb, scope, node, else_expr_result, &peer_parent->peers.at(1)->base);
}
IrBasicBlockSrc *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);
IrInstSrc **incoming_values = heap::c_allocator.allocate<IrInstSrc *>(2);
incoming_values[0] = then_expr_result;
incoming_values[1] = else_expr_result;
IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate<IrBasicBlockSrc *>(2);
incoming_blocks[0] = after_then_block;
incoming_blocks[1] = after_else_block;
IrInstSrc *phi = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, peer_parent);
return ir_expr_wrap(irb, scope, phi, result_loc);
}
static bool ir_gen_switch_prong_expr(IrBuilderSrc *irb, Scope *scope, AstNode *switch_node, AstNode *prong_node,
IrBasicBlockSrc *end_block, IrInstSrc *is_comptime, IrInstSrc *var_is_comptime,
IrInstSrc *target_value_ptr, IrInstSrc **prong_values, size_t prong_values_len,
ZigList<IrBasicBlockSrc *> *incoming_blocks, ZigList<IrInstSrc *> *incoming_values,
IrInstSrcSwitchElseVar **out_switch_else_var, LVal lval, ResultLoc *result_loc)
{
assert(switch_node->type == NodeTypeSwitchExpr);
assert(prong_node->type == NodeTypeSwitchProng);
AstNode *expr_node = prong_node->data.switch_prong.expr;
AstNode *var_symbol_node = prong_node->data.switch_prong.var_symbol;
Scope *child_scope;
if (var_symbol_node) {
assert(var_symbol_node->type == NodeTypeIdentifier);
Buf *var_name = node_identifier_buf(var_symbol_node);
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;
IrInstSrc *var_value;
if (out_switch_else_var != nullptr) {
IrInstSrcSwitchElseVar *switch_else_var = ir_build_switch_else_var(irb, scope, var_symbol_node,
target_value_ptr);
*out_switch_else_var = switch_else_var;
IrInstSrc *payload_ptr = &switch_else_var->base;
var_value = var_is_ptr ?
payload_ptr : ir_build_load_ptr(irb, scope, var_symbol_node, payload_ptr);
} else if (prong_values != nullptr) {
IrInstSrc *payload_ptr = ir_build_switch_var(irb, scope, var_symbol_node, target_value_ptr,
prong_values, prong_values_len);
var_value = var_is_ptr ?
payload_ptr : ir_build_load_ptr(irb, scope, var_symbol_node, payload_ptr);
} else {
var_value = var_is_ptr ?
target_value_ptr : ir_build_load_ptr(irb, scope, var_symbol_node, target_value_ptr);
}
build_decl_var_and_init(irb, scope, var_symbol_node, var, var_value, buf_ptr(var_name), var_is_comptime);
} else {
child_scope = scope;
}
IrInstSrc *expr_result = ir_gen_node_extra(irb, expr_node, child_scope, lval, result_loc);
if (expr_result == irb->codegen->invalid_inst_src)
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 IrInstSrc *ir_gen_switch_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
ResultLoc *result_loc)
{
assert(node->type == NodeTypeSwitchExpr);
AstNode *target_node = node->data.switch_expr.expr;
IrInstSrc *target_value_ptr = ir_gen_node_extra(irb, target_node, scope, LValPtr, nullptr);
if (target_value_ptr == irb->codegen->invalid_inst_src)
return target_value_ptr;
IrInstSrc *target_value = ir_build_switch_target(irb, scope, node, target_value_ptr);
IrBasicBlockSrc *else_block = ir_create_basic_block(irb, scope, "SwitchElse");
IrBasicBlockSrc *end_block = ir_create_basic_block(irb, scope, "SwitchEnd");
size_t prong_count = node->data.switch_expr.prongs.length;
ZigList<IrInstSrcSwitchBrCase> cases = {0};
IrInstSrc *is_comptime;
IrInstSrc *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<IrInstSrc *> incoming_values = {0};
ZigList<IrBasicBlockSrc *> incoming_blocks = {0};
ZigList<IrInstSrcCheckSwitchProngsRange> check_ranges = {0};
IrInstSrcSwitchElseVar *switch_else_var = nullptr;
ResultLocPeerParent *peer_parent = heap::c_allocator.create<ResultLocPeerParent>();
peer_parent->base.id = ResultLocIdPeerParent;
peer_parent->base.allow_write_through_const = result_loc->allow_write_through_const;
peer_parent->end_bb = end_block;
peer_parent->is_comptime = is_comptime;
peer_parent->parent = result_loc;
ir_build_reset_result(irb, scope, node, &peer_parent->base);
// First do the else and the ranges
Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime);
Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope);
AstNode *else_prong = nullptr;
AstNode *underscore_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_node->data.switch_prong.any_items_are_range) {
ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent);
IrInstSrc *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;
IrInstSrc *start_value = ir_gen_node(irb, start_node, comptime_scope);
if (start_value == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
IrInstSrc *end_value = ir_gen_node(irb, end_node, comptime_scope);
if (end_value == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
IrInstSrcCheckSwitchProngsRange *check_range = check_ranges.add_one();
check_range->start = start_value;
check_range->end = end_value;
IrInstSrc *lower_range_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpCmpGreaterOrEq,
target_value, start_value, false);
IrInstSrc *upper_range_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpCmpLessOrEq,
target_value, end_value, false);
IrInstSrc *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 {
IrInstSrc *item_value = ir_gen_node(irb, item_node, comptime_scope);
if (item_value == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
IrInstSrcCheckSwitchProngsRange *check_range = check_ranges.add_one();
check_range->start = item_value;
check_range->end = item_value;
IrInstSrc *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;
}
}
}
IrBasicBlockSrc *range_block_yes = ir_create_basic_block(irb, scope, "SwitchRangeYes");
IrBasicBlockSrc *range_block_no = ir_create_basic_block(irb, scope, "SwitchRangeNo");
assert(ok_bit);
assert(last_item_node);
IrInstSrc *br_inst = ir_mark_gen(ir_build_cond_br(irb, scope, last_item_node, ok_bit,
range_block_yes, range_block_no, is_comptime));
if (peer_parent->base.source_instruction == nullptr) {
peer_parent->base.source_instruction = br_inst;
}
if (peer_parent->peers.length > 0) {
peer_parent->peers.last()->next_bb = range_block_yes;
}
peer_parent->peers.append(this_peer_result_loc);
ir_set_cursor_at_end_and_append_block(irb, range_block_yes);
if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block,
is_comptime, var_is_comptime, target_value_ptr, nullptr, 0,
&incoming_blocks, &incoming_values, nullptr, LValNone, &this_peer_result_loc->base))
{
return irb->codegen->invalid_inst_src;
}
ir_set_cursor_at_end_and_append_block(irb, range_block_no);
} else {
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_inst_src;
}
else_prong = prong_node;
} else if (prong_item_count == 1 &&
prong_node->data.switch_prong.items.at(0)->type == NodeTypeIdentifier &&
buf_eql_str(node_identifier_buf(prong_node->data.switch_prong.items.at(0)), "_")) {
if (underscore_prong) {
ErrorMsg *msg = add_node_error(irb->codegen, prong_node,
buf_sprintf("multiple '_' prongs in switch expression"));
add_error_note(irb->codegen, msg, underscore_prong,
buf_sprintf("previous '_' prong is here"));
return irb->codegen->invalid_inst_src;
}
underscore_prong = prong_node;
} else {
continue;
}
if (underscore_prong && else_prong) {
ErrorMsg *msg = add_node_error(irb->codegen, prong_node,
buf_sprintf("else and '_' prong in switch expression"));
if (underscore_prong == prong_node)
add_error_note(irb->codegen, msg, else_prong,
buf_sprintf("else prong is here"));
else
add_error_note(irb->codegen, msg, underscore_prong,
buf_sprintf("'_' prong is here"));
return irb->codegen->invalid_inst_src;
}
ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent);
IrBasicBlockSrc *prev_block = irb->current_basic_block;
if (peer_parent->peers.length > 0) {
peer_parent->peers.last()->next_bb = else_block;
}
peer_parent->peers.append(this_peer_result_loc);
ir_set_cursor_at_end_and_append_block(irb, else_block);
if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block,
is_comptime, var_is_comptime, target_value_ptr, nullptr, 0, &incoming_blocks, &incoming_values,
&switch_else_var, LValNone, &this_peer_result_loc->base))
{
return irb->codegen->invalid_inst_src;
}
ir_set_cursor_at_end(irb, prev_block);
}
}
// 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;
if (underscore_prong == prong_node)
continue;
ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent);
IrBasicBlockSrc *prong_block = ir_create_basic_block(irb, scope, "SwitchProng");
IrInstSrc **items = heap::c_allocator.allocate<IrInstSrc *>(prong_item_count);
for (size_t item_i = 0; item_i < prong_item_count; item_i += 1) {
AstNode *item_node = prong_node->data.switch_prong.items.at(item_i);
assert(item_node->type != NodeTypeSwitchRange);
IrInstSrc *item_value = ir_gen_node(irb, item_node, comptime_scope);
if (item_value == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
IrInstSrcCheckSwitchProngsRange *check_range = check_ranges.add_one();
check_range->start = item_value;
check_range->end = item_value;
IrInstSrcSwitchBrCase *this_case = cases.add_one();
this_case->value = item_value;
this_case->block = prong_block;
items[item_i] = item_value;
}
IrBasicBlockSrc *prev_block = irb->current_basic_block;
if (peer_parent->peers.length > 0) {
peer_parent->peers.last()->next_bb = prong_block;
}
peer_parent->peers.append(this_peer_result_loc);
ir_set_cursor_at_end_and_append_block(irb, prong_block);
if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block,
is_comptime, var_is_comptime, target_value_ptr, items, prong_item_count,
&incoming_blocks, &incoming_values, nullptr, LValNone, &this_peer_result_loc->base))
{
return irb->codegen->invalid_inst_src;
}
ir_set_cursor_at_end(irb, prev_block);
}
IrInstSrc *switch_prongs_void = ir_build_check_switch_prongs(irb, scope, node, target_value,
check_ranges.items, check_ranges.length, else_prong, underscore_prong != nullptr);
IrInstSrc *br_instruction;
if (cases.length == 0) {
br_instruction = ir_build_br(irb, scope, node, else_block, is_comptime);
} else {
IrInstSrcSwitchBr *switch_br = ir_build_switch_br_src(irb, scope, node, target_value, else_block,
cases.length, cases.items, is_comptime, switch_prongs_void);
if (switch_else_var != nullptr) {
switch_else_var->switch_br = switch_br;
}
br_instruction = &switch_br->base;
}
if (peer_parent->base.source_instruction == nullptr) {
peer_parent->base.source_instruction = br_instruction;
}
for (size_t i = 0; i < peer_parent->peers.length; i += 1) {
peer_parent->peers.at(i)->base.source_instruction = peer_parent->base.source_instruction;
}
if (!else_prong && !underscore_prong) {
if (peer_parent->peers.length != 0) {
peer_parent->peers.last()->next_bb = else_block;
}
ir_set_cursor_at_end_and_append_block(irb, else_block);
ir_build_unreachable(irb, scope, node);
} else {
if (peer_parent->peers.length != 0) {
peer_parent->peers.last()->next_bb = end_block;
}
}
ir_set_cursor_at_end_and_append_block(irb, end_block);
assert(incoming_blocks.length == incoming_values.length);
IrInstSrc *result_instruction;
if (incoming_blocks.length == 0) {
result_instruction = ir_build_const_void(irb, scope, node);
} else {
result_instruction = ir_build_phi(irb, scope, node, incoming_blocks.length,
incoming_blocks.items, incoming_values.items, peer_parent);
}
return ir_lval_wrap(irb, scope, result_instruction, lval, result_loc);
}
static IrInstSrc *ir_gen_comptime(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node, LVal lval) {
assert(node->type == NodeTypeCompTime);
Scope *child_scope = create_comptime_scope(irb->codegen, node, parent_scope);
// purposefully pass null for result_loc and let EndExpr handle it
return ir_gen_node_extra(irb, node->data.comptime_expr.expr, child_scope, lval, nullptr);
}
static IrInstSrc *ir_gen_nosuspend(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node, LVal lval) {
assert(node->type == NodeTypeNoSuspend);
Scope *child_scope = create_nosuspend_scope(irb->codegen, node, parent_scope);
// purposefully pass null for result_loc and let EndExpr handle it
return ir_gen_node_extra(irb, node->data.nosuspend_expr.expr, child_scope, lval, nullptr);
}
static IrInstSrc *ir_gen_return_from_block(IrBuilderSrc *irb, Scope *break_scope, AstNode *node, ScopeBlock *block_scope) {
IrInstSrc *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;
}
IrInstSrc *result_value;
if (node->data.break_expr.expr) {
ResultLocPeer *peer_result = create_peer_result(block_scope->peer_parent);
block_scope->peer_parent->peers.append(peer_result);
result_value = ir_gen_node_extra(irb, node->data.break_expr.expr, break_scope, block_scope->lval,
&peer_result->base);
if (result_value == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
} else {
result_value = ir_build_const_void(irb, break_scope, node);
}
IrBasicBlockSrc *dest_block = block_scope->end_block;
if (!ir_gen_defers_for_block(irb, break_scope, dest_block->scope, nullptr, nullptr))
return irb->codegen->invalid_inst_src;
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 IrInstSrc *ir_gen_break(IrBuilderSrc *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_inst_src;
} else {
add_node_error(irb->codegen, node, buf_sprintf("break expression outside loop"));
return irb->codegen->invalid_inst_src;
}
} else if (search_scope->id == ScopeIdDeferExpr) {
add_node_error(irb->codegen, node, buf_sprintf("cannot break out of defer expression"));
return irb->codegen->invalid_inst_src;
} 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)))
{
this_loop_scope->name_used = true;
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);
this_block_scope->name_used = true;
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_inst_src;
}
search_scope = search_scope->parent;
}
IrInstSrc *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;
}
IrInstSrc *result_value;
if (node->data.break_expr.expr) {
ResultLocPeer *peer_result = create_peer_result(loop_scope->peer_parent);
loop_scope->peer_parent->peers.append(peer_result);
result_value = ir_gen_node_extra(irb, node->data.break_expr.expr, break_scope,
loop_scope->lval, &peer_result->base);
if (result_value == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
} else {
result_value = ir_build_const_void(irb, break_scope, node);
}
IrBasicBlockSrc *dest_block = loop_scope->break_block;
if (!ir_gen_defers_for_block(irb, break_scope, dest_block->scope, nullptr, nullptr))
return irb->codegen->invalid_inst_src;
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 IrInstSrc *ir_gen_continue(IrBuilderSrc *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_inst_src;
} else {
add_node_error(irb->codegen, node, buf_sprintf("continue expression outside loop"));
return irb->codegen->invalid_inst_src;
}
} else if (search_scope->id == ScopeIdDeferExpr) {
add_node_error(irb->codegen, node, buf_sprintf("cannot continue out of defer expression"));
return irb->codegen->invalid_inst_src;
} 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)))
{
this_loop_scope->name_used = true;
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;
}
IrInstSrc *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));
}
runtime_scopes.deinit();
IrBasicBlockSrc *dest_block = loop_scope->continue_block;
if (!ir_gen_defers_for_block(irb, continue_scope, dest_block->scope, nullptr, nullptr))
return irb->codegen->invalid_inst_src;
return ir_mark_gen(ir_build_br(irb, continue_scope, node, dest_block, is_comptime));
}
static IrInstSrc *ir_gen_error_type(IrBuilderSrc *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 IrInstSrc *ir_gen_defer(IrBuilderSrc *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 IrInstSrc *ir_gen_slice(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) {
assert(node->type == NodeTypeSliceExpr);
AstNodeSliceExpr *slice_expr = &node->data.slice_expr;
AstNode *array_node = slice_expr->array_ref_expr;
AstNode *start_node = slice_expr->start;
AstNode *end_node = slice_expr->end;
AstNode *sentinel_node = slice_expr->sentinel;
IrInstSrc *ptr_value = ir_gen_node_extra(irb, array_node, scope, LValPtr, nullptr);
if (ptr_value == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
IrInstSrc *start_value = ir_gen_node(irb, start_node, scope);
if (start_value == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
IrInstSrc *end_value;
if (end_node) {
end_value = ir_gen_node(irb, end_node, scope);
if (end_value == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
} else {
end_value = nullptr;
}
IrInstSrc *sentinel_value;
if (sentinel_node) {
sentinel_value = ir_gen_node(irb, sentinel_node, scope);
if (sentinel_value == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
} else {
sentinel_value = nullptr;
}
IrInstSrc *slice = ir_build_slice_src(irb, scope, node, ptr_value, start_value, end_value,
sentinel_value, true, result_loc);
return ir_lval_wrap(irb, scope, slice, lval, result_loc);
}
static IrInstSrc *ir_gen_catch(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node, LVal lval,
ResultLoc *result_loc)
{
assert(node->type == NodeTypeCatchExpr);
AstNode *op1_node = node->data.unwrap_err_expr.op1;
AstNode *op2_node = node->data.unwrap_err_expr.op2;
AstNode *var_node = node->data.unwrap_err_expr.symbol;
if (op2_node->type == NodeTypeUnreachable) {
if (var_node != nullptr) {
assert(var_node->type == NodeTypeIdentifier);
Buf *var_name = node_identifier_buf(var_node);
add_node_error(irb->codegen, var_node, buf_sprintf("unused variable: '%s'", buf_ptr(var_name)));
return irb->codegen->invalid_inst_src;
}
return ir_gen_catch_unreachable(irb, parent_scope, node, op1_node, lval, result_loc);
}
ScopeExpr *spill_scope = create_expr_scope(irb->codegen, op1_node, parent_scope);
spill_scope->spill_harder = true;
IrInstSrc *err_union_ptr = ir_gen_node_extra(irb, op1_node, &spill_scope->base, LValPtr, nullptr);
if (err_union_ptr == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
IrInstSrc *is_err = ir_build_test_err_src(irb, parent_scope, node, err_union_ptr, true, false);
IrInstSrc *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);
}
IrBasicBlockSrc *ok_block = ir_create_basic_block(irb, parent_scope, "UnwrapErrOk");
IrBasicBlockSrc *err_block = ir_create_basic_block(irb, parent_scope, "UnwrapErrError");
IrBasicBlockSrc *end_block = ir_create_basic_block(irb, parent_scope, "UnwrapErrEnd");
IrInstSrc *cond_br_inst = ir_build_cond_br(irb, parent_scope, node, is_err, err_block, ok_block, is_comptime);
ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, ok_block, end_block, result_loc,
is_comptime);
ir_set_cursor_at_end_and_append_block(irb, err_block);
Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, &spill_scope->base, is_comptime);
Scope *err_scope;
if (var_node) {
assert(var_node->type == NodeTypeIdentifier);
Buf *var_name = node_identifier_buf(var_node);
bool is_const = true;
bool is_shadowable = false;
ZigVar *var = ir_create_var(irb, node, subexpr_scope, var_name,
is_const, is_const, is_shadowable, is_comptime);
err_scope = var->child_scope;
IrInstSrc *err_ptr = ir_build_unwrap_err_code_src(irb, err_scope, node, err_union_ptr);
IrInstSrc *err_value = ir_build_load_ptr(irb, err_scope, var_node, err_ptr);
build_decl_var_and_init(irb, err_scope, var_node, var, err_value, buf_ptr(var_name), is_comptime);
} else {
err_scope = subexpr_scope;
}
IrInstSrc *err_result = ir_gen_node_extra(irb, op2_node, err_scope, LValNone, &peer_parent->peers.at(0)->base);
if (err_result == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
IrBasicBlockSrc *after_err_block = irb->current_basic_block;
if (!instr_is_unreachable(err_result))
ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime));
ir_set_cursor_at_end_and_append_block(irb, ok_block);
IrInstSrc *unwrapped_ptr = ir_build_unwrap_err_payload_src(irb, parent_scope, node, err_union_ptr, false, false);
IrInstSrc *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr);
ir_build_end_expr(irb, parent_scope, node, unwrapped_payload, &peer_parent->peers.at(1)->base);
IrBasicBlockSrc *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);
IrInstSrc **incoming_values = heap::c_allocator.allocate<IrInstSrc *>(2);
incoming_values[0] = err_result;
incoming_values[1] = unwrapped_payload;
IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate<IrBasicBlockSrc *>(2);
incoming_blocks[0] = after_err_block;
incoming_blocks[1] = after_ok_block;
IrInstSrc *phi = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values, peer_parent);
return ir_lval_wrap(irb, parent_scope, phi, lval, result_loc);
}
static bool render_instance_name_recursive(CodeGen *codegen, Buf *name, Scope *outer_scope, Scope *inner_scope) {
if (inner_scope == nullptr || inner_scope == outer_scope) return false;
bool need_comma = render_instance_name_recursive(codegen, name, outer_scope, inner_scope->parent);
if (inner_scope->id != ScopeIdVarDecl)
return need_comma;
ScopeVarDecl *var_scope = (ScopeVarDecl *)inner_scope;
if (need_comma)
buf_append_char(name, ',');
// TODO: const ptr reinterpret here to make the var type agree with the value?
render_const_value(codegen, name, var_scope->var->const_value);
return true;
}
Buf *get_anon_type_name(CodeGen *codegen, Stage1Zir *exec, const char *kind_name,
Scope *scope, AstNode *source_node, Buf *out_bare_name)
{
if (exec != nullptr && exec->name) {
ZigType *import = get_scope_import(scope);
Buf *namespace_name = buf_alloc();
append_namespace_qualification(codegen, namespace_name, import);
buf_append_buf(namespace_name, exec->name);
buf_init_from_buf(out_bare_name, exec->name);
return namespace_name;
} else if (exec != nullptr && 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_alloc();
append_namespace_qualification(codegen, namespace_name, import);
RootStruct *root_struct = source_node->owner->data.structure.root_struct;
TokenLoc tok_loc = root_struct->token_locs[source_node->main_token];
buf_appendf(namespace_name, "%s:%u:%u", kind_name,
tok_loc.line + 1, tok_loc.column + 1);
buf_init_from_buf(out_bare_name, namespace_name);
return namespace_name;
}
}
static IrInstSrc *ir_gen_container_decl(IrBuilderSrc *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 = heap::c_allocator.create<TldContainer>();
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);
}
static IrInstSrc *ir_gen_err_set_decl(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node) {
assert(node->type == NodeTypeErrorSetDecl);
uint32_t err_count = node->data.err_set_decl.decls.length;
Buf bare_name = BUF_INIT;
Buf *type_name = get_anon_type_name(irb->codegen, irb->exec, "error", parent_scope, node, &bare_name);
ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet);
buf_init_from_buf(&err_set_type->name, type_name);
err_set_type->data.error_set.err_count = err_count;
err_set_type->size_in_bits = irb->codegen->builtin_types.entry_global_error_set->size_in_bits;
err_set_type->abi_align = irb->codegen->builtin_types.entry_global_error_set->abi_align;
err_set_type->abi_size = irb->codegen->builtin_types.entry_global_error_set->abi_size;
err_set_type->data.error_set.errors = heap::c_allocator.allocate<ErrorTableEntry *>(err_count);
size_t errors_count = irb->codegen->errors_by_index.length + err_count;
ErrorTableEntry **errors = heap::c_allocator.allocate<ErrorTableEntry *>(errors_count);
for (uint32_t i = 0; i < err_count; i += 1) {
AstNode *field_node = node->data.err_set_decl.decls.at(i);
AstNode *symbol_node = ast_field_to_symbol_node(field_node);
Buf *err_name = node_identifier_buf(symbol_node);
ErrorTableEntry *err = heap::c_allocator.create<ErrorTableEntry>();
err->decl_node = field_node;
buf_init_from_buf(&err->name, err_name);
auto existing_entry = irb->codegen->error_table.put_unique(err_name, err);
if (existing_entry) {
err->value = existing_entry->value->value;
} else {
size_t error_value_count = irb->codegen->errors_by_index.length;
assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)irb->codegen->err_tag_type->data.integral.bit_count));
err->value = error_value_count;
irb->codegen->errors_by_index.append(err);
}
err_set_type->data.error_set.errors[i] = err;
ErrorTableEntry *prev_err = errors[err->value];
if (prev_err != nullptr) {
ErrorMsg *msg = add_node_error(irb->codegen, ast_field_to_symbol_node(err->decl_node),
buf_sprintf("duplicate error: '%s'", buf_ptr(&err->name)));
add_error_note(irb->codegen, msg, ast_field_to_symbol_node(prev_err->decl_node),
buf_sprintf("other error here"));
return irb->codegen->invalid_inst_src;
}
errors[err->value] = err;
}
heap::c_allocator.deallocate(errors, errors_count);
return ir_build_const_type(irb, parent_scope, node, err_set_type);
}
static IrInstSrc *ir_gen_fn_proto(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node) {
assert(node->type == NodeTypeFnProto);
size_t param_count = node->data.fn_proto.params.length;
IrInstSrc **param_types = heap::c_allocator.allocate<IrInstSrc*>(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.anytype_token == 0) {
AstNode *type_node = param_node->data.param_decl.type;
IrInstSrc *type_value = ir_gen_node(irb, type_node, parent_scope);
if (type_value == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
param_types[i] = type_value;
} else {
param_types[i] = nullptr;
}
}
IrInstSrc *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_inst_src)
return irb->codegen->invalid_inst_src;
}
IrInstSrc *callconv_value = nullptr;
if (node->data.fn_proto.callconv_expr != nullptr) {
callconv_value = ir_gen_node(irb, node->data.fn_proto.callconv_expr, parent_scope);
if (callconv_value == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
}
IrInstSrc *return_type;
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_inst_src)
return irb->codegen->invalid_inst_src;
}
return ir_build_fn_proto(irb, parent_scope, node, param_types, align_value, callconv_value, return_type, is_var_args);
}
static IrInstSrc *ir_gen_resume(IrBuilderSrc *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeResume);
IrInstSrc *target_inst = ir_gen_node_extra(irb, node->data.resume_expr.expr, scope, LValPtr, nullptr);
if (target_inst == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
return ir_build_resume_src(irb, scope, node, target_inst);
}
static IrInstSrc *ir_gen_await_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval,
ResultLoc *result_loc)
{
assert(node->type == NodeTypeAwaitExpr);
bool is_nosuspend = get_scope_nosuspend(scope) != nullptr;
AstNode *expr_node = node->data.await_expr.expr;
if (expr_node->type == NodeTypeFnCallExpr && expr_node->data.fn_call_expr.modifier == CallModifierBuiltin) {
AstNode *fn_ref_expr = expr_node->data.fn_call_expr.fn_ref_expr;
Buf *name = node_identifier_buf(fn_ref_expr);
auto entry = irb->codegen->builtin_fn_table.maybe_get(name);
if (entry != nullptr) {
BuiltinFnEntry *builtin_fn = entry->value;
if (builtin_fn->id == BuiltinFnIdAsyncCall) {
return ir_gen_async_call(irb, scope, node, expr_node, lval, result_loc);
}
}
}
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_inst_src;
}
ScopeSuspend *existing_suspend_scope = get_scope_suspend(scope);
if (existing_suspend_scope) {
if (!existing_suspend_scope->reported_err) {
ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot await inside suspend block"));
add_error_note(irb->codegen, msg, existing_suspend_scope->base.source_node, buf_sprintf("suspend block here"));
existing_suspend_scope->reported_err = true;
}
return irb->codegen->invalid_inst_src;
}
IrInstSrc *target_inst = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr);
if (target_inst == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
IrInstSrc *await_inst = ir_build_await_src(irb, scope, node, target_inst, result_loc, is_nosuspend);
return ir_lval_wrap(irb, scope, await_inst, lval, result_loc);
}
static IrInstSrc *ir_gen_suspend(IrBuilderSrc *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_inst_src;
}
if (get_scope_nosuspend(parent_scope) != nullptr) {
add_node_error(irb->codegen, node, buf_sprintf("suspend in nosuspend scope"));
return irb->codegen->invalid_inst_src;
}
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_inst_src;
}
IrInstSrcSuspendBegin *begin = ir_build_suspend_begin_src(irb, parent_scope, node);
ScopeSuspend *suspend_scope = create_suspend_scope(irb->codegen, node, parent_scope);
Scope *child_scope = &suspend_scope->base;
IrInstSrc *susp_res = ir_gen_node(irb, node->data.suspend.block, child_scope);
if (susp_res == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.suspend.block, susp_res));
return ir_mark_gen(ir_build_suspend_finish_src(irb, parent_scope, node, begin));
}
static IrInstSrc *ir_gen_node_raw(IrBuilderSrc *irb, AstNode *node, Scope *scope,
LVal lval, ResultLoc *result_loc)
{
assert(scope);
switch (node->type) {
case NodeTypeStructValueField:
case NodeTypeParamDecl:
case NodeTypeUsingNamespace:
case NodeTypeSwitchProng:
case NodeTypeSwitchRange:
case NodeTypeStructField:
case NodeTypeErrorSetField:
case NodeTypeFnDef:
case NodeTypeTestDecl:
zig_unreachable();
case NodeTypeBlock:
return ir_gen_block(irb, scope, node, lval, result_loc);
case NodeTypeGroupedExpr:
return ir_gen_node_raw(irb, node->data.grouped_expr, scope, lval, result_loc);
case NodeTypeBinOpExpr:
return ir_gen_bin_op(irb, scope, node, lval, result_loc);
case NodeTypeIntLiteral:
return ir_lval_wrap(irb, scope, ir_gen_int_lit(irb, scope, node), lval, result_loc);
case NodeTypeFloatLiteral:
return ir_lval_wrap(irb, scope, ir_gen_float_lit(irb, scope, node), lval, result_loc);
case NodeTypeCharLiteral:
return ir_lval_wrap(irb, scope, ir_gen_char_lit(irb, scope, node), lval, result_loc);
case NodeTypeIdentifier:
return ir_gen_symbol(irb, scope, node, lval, result_loc);
case NodeTypeFnCallExpr:
return ir_gen_fn_call(irb, scope, node, lval, result_loc);
case NodeTypeIfBoolExpr:
return ir_gen_if_bool_expr(irb, scope, node, lval, result_loc);
case NodeTypePrefixOpExpr:
return ir_gen_prefix_op_expr(irb, scope, node, lval, result_loc);
case NodeTypeContainerInitExpr:
return ir_gen_container_init_expr(irb, scope, node, lval, result_loc);
case NodeTypeVariableDeclaration:
return ir_gen_var_decl(irb, scope, node);
case NodeTypeWhileExpr:
return ir_gen_while_expr(irb, scope, node, lval, result_loc);
case NodeTypeForExpr:
return ir_gen_for_expr(irb, scope, node, lval, result_loc);
case NodeTypeArrayAccessExpr:
return ir_gen_array_access(irb, scope, node, lval, result_loc);
case NodeTypeReturnExpr:
return ir_gen_return(irb, scope, node, lval, result_loc);
case NodeTypeFieldAccessExpr:
{
IrInstSrc *ptr_instruction = ir_gen_field_access(irb, scope, node);
if (ptr_instruction == irb->codegen->invalid_inst_src)
return ptr_instruction;
if (lval == LValPtr || lval == LValAssign)
return ptr_instruction;
IrInstSrc *load_ptr = ir_build_load_ptr(irb, scope, node, ptr_instruction);
return ir_expr_wrap(irb, scope, load_ptr, result_loc);
}
case NodeTypePtrDeref: {
AstNode *expr_node = node->data.ptr_deref_expr.target;
LVal child_lval = lval;
if (child_lval == LValAssign)
child_lval = LValPtr;
IrInstSrc *value = ir_gen_node_extra(irb, expr_node, scope, child_lval, nullptr);
if (value == irb->codegen->invalid_inst_src)
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
IrInstSrc *un_op = ir_build_un_op_lval(irb, scope, node, IrUnOpDereference, value, lval, result_loc);
return ir_expr_wrap(irb, scope, un_op, result_loc);
}
case NodeTypeUnwrapOptional: {
AstNode *expr_node = node->data.unwrap_optional.expr;
IrInstSrc *maybe_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr);
if (maybe_ptr == irb->codegen->invalid_inst_src)
return irb->codegen->invalid_inst_src;
IrInstSrc *unwrapped_ptr = ir_build_optional_unwrap_ptr(irb, scope, node, maybe_ptr, true );
if (lval == LValPtr || lval == LValAssign)
return unwrapped_ptr;
IrInstSrc *load_ptr = ir_build_load_ptr(irb, scope, node, unwrapped_ptr);
return ir_expr_wrap(irb, scope, load_ptr, result_loc);
}
case NodeTypeBoolLiteral:
return ir_lval_wrap(irb, scope, ir_gen_bool_literal(irb, scope, node), lval, result_loc);
case NodeTypeArrayType:
return ir_lval_wrap(irb, scope, ir_gen_array_type(irb, scope, node), lval, result_loc);
case NodeTypePointerType:
return ir_lval_wrap(irb, scope, ir_gen_pointer_type(irb, scope, node), lval, result_loc);
case NodeTypeAnyFrameType:
return ir_lval_wrap(irb, scope, ir_gen_anyframe_type(irb, scope, node), lval, result_loc);
case NodeTypeStringLiteral:
return ir_lval_wrap(irb, scope, ir_gen_string_literal(irb, scope, node), lval, result_loc);
case NodeTypeUndefinedLiteral:
return ir_lval_wrap(irb, scope, ir_gen_undefined_literal(irb, scope, node), lval, result_loc);
case NodeTypeAsmExpr:
return ir_lval_wrap(irb, scope, ir_gen_asm_expr(irb, scope, node), lval, result_loc);
case NodeTypeNullLiteral:
return ir_lval_wrap(irb, scope, ir_gen_null_literal(irb, scope, node), lval, result_loc);
case NodeTypeIfErrorExpr:
return ir_gen_if_err_expr(irb, scope, node, lval, result_loc);
case NodeTypeIfOptional:
return ir_gen_if_optional_expr(irb, scope, node, lval, result_loc);
case NodeTypeSwitchExpr:
return ir_gen_switch_expr(irb, scope, node, lval, result_loc);
case NodeTypeCompTime:
return ir_expr_wrap(irb, scope, ir_gen_comptime(irb, scope, node, lval), result_loc);
case NodeTypeNoSuspend:
return ir_expr_wrap(irb, scope, ir_gen_nosuspend(irb, scope, node, lval), result_loc);
case NodeTypeErrorType:
return ir_lval_wrap(irb, scope, ir_gen_error_type(irb, scope, node), lval, result_loc);
case NodeTypeBreak:
return ir_lval_wrap(irb, scope, ir_gen_break(irb, scope, node), lval, result_loc);
case NodeTypeContinue:
return ir_lval_wrap(irb, scope, ir_gen_continue(irb, scope, node), lval, result_loc);
case NodeTypeUnreachable:
return ir_build_unreachable(irb, scope, node);
case NodeTypeDefer:
return ir_lval_wrap(irb, scope, ir_gen_defer(irb, scope, node), lval, result_loc);
case NodeTypeSliceExpr:
return ir_gen_slice(irb, scope, node, lval, result_loc);
case NodeTypeCatchExpr:
return ir_gen_catch(irb, scope, node, lval, result_loc);
case NodeTypeContainerDecl:
return ir_lval_wrap(irb, scope, ir_gen_container_decl(irb, scope, node), lval, result_loc);
case NodeTypeFnProto:
return ir_lval_wrap(irb, scope, ir_gen_fn_proto(irb, scope, node), lval, result_loc);
case NodeTypeErrorSetDecl:
return ir_lval_wrap(irb, scope, ir_gen_err_set_decl(irb, scope, node), lval, result_loc);
case NodeTypeResume:
return ir_lval_wrap(irb, scope, ir_gen_resume(irb, scope, node), lval, result_loc);
case NodeTypeAwaitExpr:
return ir_gen_await_expr(irb, scope, node, lval, result_loc);
case NodeTypeSuspend:
return ir_lval_wrap(irb, scope, ir_gen_suspend(irb, scope, node), lval, result_loc);
case NodeTypeEnumLiteral:
return ir_lval_wrap(irb, scope, ir_gen_enum_literal(irb, scope, node), lval, result_loc);
case NodeTypeInferredArrayType:
add_node_error(irb->codegen, node,
buf_sprintf("inferred array size invalid here"));
return irb->codegen->invalid_inst_src;
case NodeTypeAnyTypeField:
return ir_lval_wrap(irb, scope,
ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_anytype), lval, result_loc);
}
zig_unreachable();
}
ResultLoc *no_result_loc(void) {
ResultLocNone *result_loc_none = heap::c_allocator.create<ResultLocNone>();
result_loc_none->base.id = ResultLocIdNone;
return &result_loc_none->base;
}
static IrInstSrc *ir_gen_node_extra(IrBuilderSrc *irb, AstNode *node, Scope *scope, LVal lval,
ResultLoc *result_loc)
{
if (lval == LValAssign) {
switch (node->type) {
case NodeTypeStructValueField:
case NodeTypeParamDecl:
case NodeTypeUsingNamespace:
case NodeTypeSwitchProng:
case NodeTypeSwitchRange:
case NodeTypeStructField:
case NodeTypeErrorSetField:
case NodeTypeFnDef:
case NodeTypeTestDecl:
zig_unreachable();
// cannot be assigned to
case NodeTypeBlock:
case NodeTypeGroupedExpr:
case NodeTypeBinOpExpr:
case NodeTypeIntLiteral:
case NodeTypeFloatLiteral:
case NodeTypeCharLiteral:
case NodeTypeIfBoolExpr:
case NodeTypeContainerInitExpr:
case NodeTypeVariableDeclaration:
case NodeTypeWhileExpr:
case NodeTypeForExpr:
case NodeTypeReturnExpr:
case NodeTypeBoolLiteral:
case NodeTypeArrayType:
case NodeTypePointerType:
case NodeTypeAnyFrameType:
case NodeTypeStringLiteral:
case NodeTypeUndefinedLiteral:
case NodeTypeAsmExpr:
case NodeTypeNullLiteral:
case NodeTypeIfErrorExpr:
case NodeTypeIfOptional:
case NodeTypeSwitchExpr:
case NodeTypeCompTime:
case NodeTypeNoSuspend:
case NodeTypeErrorType:
case NodeTypeBreak:
case NodeTypeContinue:
case NodeTypeUnreachable:
case NodeTypeDefer:
case NodeTypeSliceExpr:
case NodeTypeCatchExpr:
case NodeTypeContainerDecl:
case NodeTypeFnProto:
case NodeTypeErrorSetDecl:
case NodeTypeResume:
case NodeTypeAwaitExpr:
case NodeTypeSuspend:
case NodeTypeEnumLiteral:
case NodeTypeInferredArrayType:
case NodeTypeAnyTypeField:
case NodeTypePrefixOpExpr:
add_node_error(irb->codegen, node,
buf_sprintf("invalid left-hand side to assignment"));
return irb->codegen->invalid_inst_src;
// @field can be assigned to
case NodeTypeFnCallExpr:
if (node->data.fn_call_expr.modifier == CallModifierBuiltin) {
AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
Buf *name = node_identifier_buf(fn_ref_expr);
auto entry = irb->codegen->builtin_fn_table.maybe_get(name);
if (!entry) {
add_node_error(irb->codegen, node,
buf_sprintf("invalid builtin function: '%s'", buf_ptr(name)));
return irb->codegen->invalid_inst_src;
}
if (entry->value->id == BuiltinFnIdField) {
break;
}
}
add_node_error(irb->codegen, node,
buf_sprintf("invalid left-hand side to assignment"));
return irb->codegen->invalid_inst_src;
// can be assigned to
case NodeTypeUnwrapOptional:
case NodeTypePtrDeref:
case NodeTypeFieldAccessExpr:
case NodeTypeArrayAccessExpr:
case NodeTypeIdentifier:
break;
}
}
if (result_loc == nullptr) {
// Create a result location indicating there is none - but if one gets created
// it will be properly distributed.
result_loc = no_result_loc();
ir_build_reset_result(irb, scope, node, result_loc);
}
Scope *child_scope;
if (irb->exec->is_inline ||
(irb->exec->fn_entry != nullptr && irb->exec->fn_entry->child_scope == scope))
{
child_scope = scope;
} else {
child_scope = &create_expr_scope(irb->codegen, node, scope)->base;
}
IrInstSrc *result = ir_gen_node_raw(irb, node, child_scope, lval, result_loc);
if (result == irb->codegen->invalid_inst_src) {
if (irb->exec->first_err_trace_msg == nullptr) {
irb->exec->first_err_trace_msg = irb->codegen->trace_err;
}
}
return result;
}
static IrInstSrc *ir_gen_node(IrBuilderSrc *irb, AstNode *node, Scope *scope) {
return ir_gen_node_extra(irb, node, scope, LValNone, nullptr);
}
bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, Stage1Zir *ir_executable) {
assert(node->owner);
IrBuilderSrc ir_builder = {0};
IrBuilderSrc *irb = &ir_builder;
irb->codegen = codegen;
irb->exec = ir_executable;
irb->main_block_node = node;
IrBasicBlockSrc *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);
IrInstSrc *result = ir_gen_node_extra(irb, node, scope, LValNone, nullptr);
if (result == irb->codegen->invalid_inst_src)
return false;
if (irb->exec->first_err_trace_msg != nullptr) {
codegen->trace_err = irb->exec->first_err_trace_msg;
return false;
}
if (!instr_is_unreachable(result)) {
ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, result->base.source_node, result, nullptr));
// no need for save_err_ret_addr because this cannot return error
ResultLocReturn *result_loc_ret = heap::c_allocator.create<ResultLocReturn>();
result_loc_ret->base.id = ResultLocIdReturn;
ir_build_reset_result(irb, scope, node, &result_loc_ret->base);
ir_mark_gen(ir_build_end_expr(irb, scope, node, result, &result_loc_ret->base));
ir_mark_gen(ir_build_return_src(irb, scope, result->base.source_node, result));
}
return true;
}
bool ir_gen_fn(CodeGen *codegen, ZigFn *fn_entry) {
assert(fn_entry);
Stage1Zir *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);
}
void invalidate_exec(Stage1Zir *exec, ErrorMsg *msg) {
if (exec->first_err_trace_msg != nullptr)
return;
exec->first_err_trace_msg = msg;
for (size_t i = 0; i < exec->tld_list.length; i += 1) {
exec->tld_list.items[i]->resolution = TldResolutionInvalid;
}
}
AstNode *ast_field_to_symbol_node(AstNode *err_set_field_node) {
if (err_set_field_node->type == NodeTypeIdentifier) {
return err_set_field_node;
} else if (err_set_field_node->type == NodeTypeErrorSetField) {
assert(err_set_field_node->data.err_set_field.field_name->type == NodeTypeIdentifier);
return err_set_field_node->data.err_set_field.field_name;
} else {
return err_set_field_node;
}
}
void ir_add_call_stack_errors_gen(CodeGen *codegen, Stage1Air *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"));
ir_add_call_stack_errors_gen(codegen, exec->parent_exec, err_msg, limit - 1);
}