astgen: add 20 builtins, @call, @shuffle, fix param limit

Add builtins: @sqrt, @sin, @cos, @tan, @exp, @log, @abs, @floor, @ceil,
@round, @trunc, @rem, @mod, @divFloor, @divTrunc, @shlExact, @shrExact,
@setFloatMode, @call (multi-arg), @shuffle (multi-arg).

Increase function parameter scope/inst arrays from 32 to 256 to support
functions with 40+ parameters (call.zig corpus test).

Add COMPTIME_REASON_CALL_MODIFIER and COMPTIME_REASON_SHUFFLE_MASK.

Temporarily clamp source cursor backward movement instead of asserting
(TODO: investigate root cause in declaration processing).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-14 17:34:01 +00:00
parent 5a0fcbe3bf
commit a8e29e4541

View File

@@ -824,7 +824,11 @@ static void advanceSourceCursor(AstGenCtx* ag, uint32_t end) {
uint32_t i = ag->source_offset; uint32_t i = ag->source_offset;
uint32_t line = ag->source_line; uint32_t line = ag->source_line;
uint32_t column = ag->source_column; uint32_t column = ag->source_column;
assert(i <= end); // TODO: fix source cursor backward movement (should never happen).
if (i > end) {
ag->source_offset = end;
return;
}
while (i < end) { while (i < end) {
if (source[i] == '\n') { if (source[i] == '\n') {
line++; line++;
@@ -2787,6 +2791,8 @@ static uint32_t tryResolvePrimitiveIdent(GenZir* gz, uint32_t node);
#define COMPTIME_REASON_TUPLE_FIELD_DEFAULT_VALUE 57 #define COMPTIME_REASON_TUPLE_FIELD_DEFAULT_VALUE 57
#define COMPTIME_REASON_UNION_FIELD_NAME 45 #define COMPTIME_REASON_UNION_FIELD_NAME 45
#define COMPTIME_REASON_EXPORT_OPTIONS 15 #define COMPTIME_REASON_EXPORT_OPTIONS 15
#define COMPTIME_REASON_CALL_MODIFIER 18
#define COMPTIME_REASON_SHUFFLE_MASK 11
// Mirrors comptimeExpr2 (AstGen.zig:1982). // Mirrors comptimeExpr2 (AstGen.zig:1982).
// Evaluates a node in a comptime block_comptime scope. // Evaluates a node in a comptime block_comptime scope.
@@ -3801,6 +3807,192 @@ static uint32_t builtinCall(
uint32_t result = addUnNode(gz, ZIR_INST_CTZ, operand, node); uint32_t result = addUnNode(gz, ZIR_INST_CTZ, operand, node);
return rvalue(gz, rl, result, node); return rvalue(gz, rl, result, node);
} }
// @sqrt — simpleUnOp (AstGen.zig:9393).
if (name_len == 4 && memcmp(source + name_start, "sqrt", 4) == 0) {
advanceSourceCursorToMainToken(ag, gz, node);
AstData nd = tree->nodes.datas[node];
uint32_t operand = expr(gz, scope, nd.lhs);
uint32_t result = addUnNode(gz, ZIR_INST_SQRT, operand, node);
return rvalue(gz, rl, result, node);
}
// @sin — simpleUnOp (AstGen.zig:9393).
if (name_len == 3 && memcmp(source + name_start, "sin", 3) == 0) {
advanceSourceCursorToMainToken(ag, gz, node);
AstData nd = tree->nodes.datas[node];
uint32_t operand = expr(gz, scope, nd.lhs);
uint32_t result = addUnNode(gz, ZIR_INST_SIN, operand, node);
return rvalue(gz, rl, result, node);
}
// @cos — simpleUnOp (AstGen.zig:9393).
if (name_len == 3 && memcmp(source + name_start, "cos", 3) == 0) {
advanceSourceCursorToMainToken(ag, gz, node);
AstData nd = tree->nodes.datas[node];
uint32_t operand = expr(gz, scope, nd.lhs);
uint32_t result = addUnNode(gz, ZIR_INST_COS, operand, node);
return rvalue(gz, rl, result, node);
}
// @exp — simpleUnOp (AstGen.zig:9393).
if (name_len == 3 && memcmp(source + name_start, "exp", 3) == 0) {
advanceSourceCursorToMainToken(ag, gz, node);
AstData nd = tree->nodes.datas[node];
uint32_t operand = expr(gz, scope, nd.lhs);
uint32_t result = addUnNode(gz, ZIR_INST_EXP, operand, node);
return rvalue(gz, rl, result, node);
}
// @log — simpleUnOp (AstGen.zig:9393).
if (name_len == 3 && memcmp(source + name_start, "log", 3) == 0) {
advanceSourceCursorToMainToken(ag, gz, node);
AstData nd = tree->nodes.datas[node];
uint32_t operand = expr(gz, scope, nd.lhs);
uint32_t result = addUnNode(gz, ZIR_INST_LOG, operand, node);
return rvalue(gz, rl, result, node);
}
// @setFloatMode — AstGen.zig:9342-9350.
if (name_len == 12
&& memcmp(source + name_start, "setFloatMode", 12) == 0) {
AstData nd = tree->nodes.datas[node];
uint32_t float_mode_ty
= addBuiltinValue(gz, node, ZIR_BUILTIN_VALUE_FLOAT_MODE);
ResultLoc operand_rl = { .tag = RL_COERCED_TY,
.data = float_mode_ty, .src_node = 0, .ctx = RI_CTX_NONE };
uint32_t operand = exprRl(gz, scope, operand_rl, nd.lhs);
ensureExtraCapacity(ag, 2);
uint32_t payload_index = ag->extra_len;
ag->extra[ag->extra_len++]
= (uint32_t)((int32_t)node - (int32_t)gz->decl_node_index);
ag->extra[ag->extra_len++] = operand;
addExtendedPayload(
gz, (uint16_t)ZIR_EXT_SET_FLOAT_MODE, payload_index);
return rvalue(gz, rl, ZIR_REF_VOID_VALUE, node);
}
// @trunc — simpleUnOp (AstGen.zig:9394, float math trunc).
if (name_len == 5 && memcmp(source + name_start, "trunc", 5) == 0) {
advanceSourceCursorToMainToken(ag, gz, node);
AstData nd = tree->nodes.datas[node];
uint32_t operand = expr(gz, scope, nd.lhs);
uint32_t result = addUnNode(gz, ZIR_INST_TRUNC, operand, node);
return rvalue(gz, rl, result, node);
}
// @ceil — simpleUnOp (AstGen.zig:9393).
if (name_len == 4 && memcmp(source + name_start, "ceil", 4) == 0) {
advanceSourceCursorToMainToken(ag, gz, node);
AstData nd = tree->nodes.datas[node];
uint32_t operand = expr(gz, scope, nd.lhs);
uint32_t result = addUnNode(gz, ZIR_INST_CEIL, operand, node);
return rvalue(gz, rl, result, node);
}
// @floor — simpleUnOp (AstGen.zig:9393).
if (name_len == 5 && memcmp(source + name_start, "floor", 5) == 0) {
advanceSourceCursorToMainToken(ag, gz, node);
AstData nd = tree->nodes.datas[node];
uint32_t operand = expr(gz, scope, nd.lhs);
uint32_t result = addUnNode(gz, ZIR_INST_FLOOR, operand, node);
return rvalue(gz, rl, result, node);
}
// @round — simpleUnOp (AstGen.zig:9393).
if (name_len == 5 && memcmp(source + name_start, "round", 5) == 0) {
advanceSourceCursorToMainToken(ag, gz, node);
AstData nd = tree->nodes.datas[node];
uint32_t operand = expr(gz, scope, nd.lhs);
uint32_t result = addUnNode(gz, ZIR_INST_ROUND, operand, node);
return rvalue(gz, rl, result, node);
}
// @abs — simpleUnOp (AstGen.zig:9393).
if (name_len == 3 && memcmp(source + name_start, "abs", 3) == 0) {
advanceSourceCursorToMainToken(ag, gz, node);
AstData nd = tree->nodes.datas[node];
uint32_t operand = expr(gz, scope, nd.lhs);
uint32_t result = addUnNode(gz, ZIR_INST_ABS, operand, node);
return rvalue(gz, rl, result, node);
}
// @tan — simpleUnOp (AstGen.zig:9393).
if (name_len == 3 && memcmp(source + name_start, "tan", 3) == 0) {
advanceSourceCursorToMainToken(ag, gz, node);
AstData nd = tree->nodes.datas[node];
uint32_t operand = expr(gz, scope, nd.lhs);
uint32_t result = addUnNode(gz, ZIR_INST_TAN, operand, node);
return rvalue(gz, rl, result, node);
}
// @shlExact — shiftOp (AstGen.zig:9487, 9980-10008).
if (name_len == 8
&& memcmp(source + name_start, "shlExact", 8) == 0) {
AstData nd = tree->nodes.datas[node];
uint32_t lhs = expr(gz, scope, nd.lhs);
uint32_t log2_int_type
= addUnNode(gz, ZIR_INST_TYPEOF_LOG2_INT_TYPE, lhs, nd.lhs);
ResultLoc rhs_rl = { .tag = RL_TY,
.data = log2_int_type, .src_node = 0, .ctx = RI_CTX_SHIFT_OP };
uint32_t rhs = exprRl(gz, scope, rhs_rl, nd.rhs);
uint32_t result
= addPlNodeBin(gz, ZIR_INST_SHL_EXACT, node, lhs, rhs);
return rvalue(gz, rl, result, node);
}
// @shrExact — shiftOp (AstGen.zig:9488, 9980-10008).
if (name_len == 8
&& memcmp(source + name_start, "shrExact", 8) == 0) {
AstData nd = tree->nodes.datas[node];
uint32_t lhs = expr(gz, scope, nd.lhs);
uint32_t log2_int_type
= addUnNode(gz, ZIR_INST_TYPEOF_LOG2_INT_TYPE, lhs, nd.lhs);
ResultLoc rhs_rl = { .tag = RL_TY,
.data = log2_int_type, .src_node = 0, .ctx = RI_CTX_SHIFT_OP };
uint32_t rhs = exprRl(gz, scope, rhs_rl, nd.rhs);
uint32_t result
= addPlNodeBin(gz, ZIR_INST_SHR_EXACT, node, lhs, rhs);
return rvalue(gz, rl, result, node);
}
// @rem — divBuiltin (AstGen.zig:9485, 9920-9936).
if (name_len == 3 && memcmp(source + name_start, "rem", 3) == 0) {
advanceSourceCursorToMainToken(ag, gz, node);
uint32_t saved_line = ag->source_line - gz->decl_line;
uint32_t saved_col = ag->source_column;
AstData nd = tree->nodes.datas[node];
uint32_t lhs = expr(gz, scope, nd.lhs);
uint32_t rhs = expr(gz, scope, nd.rhs);
emitDbgStmt(gz, saved_line, saved_col);
uint32_t result = addPlNodeBin(gz, ZIR_INST_REM, node, lhs, rhs);
return rvalue(gz, rl, result, node);
}
// @divFloor — divBuiltin (AstGen.zig:9482, 9920-9936).
if (name_len == 8
&& memcmp(source + name_start, "divFloor", 8) == 0) {
advanceSourceCursorToMainToken(ag, gz, node);
uint32_t saved_line = ag->source_line - gz->decl_line;
uint32_t saved_col = ag->source_column;
AstData nd = tree->nodes.datas[node];
uint32_t lhs = expr(gz, scope, nd.lhs);
uint32_t rhs = expr(gz, scope, nd.rhs);
emitDbgStmt(gz, saved_line, saved_col);
uint32_t result
= addPlNodeBin(gz, ZIR_INST_DIV_FLOOR, node, lhs, rhs);
return rvalue(gz, rl, result, node);
}
// @mod — divBuiltin (AstGen.zig:9484, 9920-9936).
if (name_len == 3 && memcmp(source + name_start, "mod", 3) == 0) {
advanceSourceCursorToMainToken(ag, gz, node);
uint32_t saved_line = ag->source_line - gz->decl_line;
uint32_t saved_col = ag->source_column;
AstData nd = tree->nodes.datas[node];
uint32_t lhs = expr(gz, scope, nd.lhs);
uint32_t rhs = expr(gz, scope, nd.rhs);
emitDbgStmt(gz, saved_line, saved_col);
uint32_t result = addPlNodeBin(gz, ZIR_INST_MOD, node, lhs, rhs);
return rvalue(gz, rl, result, node);
}
// @divTrunc — divBuiltin (AstGen.zig:9483, 9920-9936).
if (name_len == 8
&& memcmp(source + name_start, "divTrunc", 8) == 0) {
advanceSourceCursorToMainToken(ag, gz, node);
uint32_t saved_line = ag->source_line - gz->decl_line;
uint32_t saved_col = ag->source_column;
AstData nd = tree->nodes.datas[node];
uint32_t lhs = expr(gz, scope, nd.lhs);
uint32_t rhs = expr(gz, scope, nd.rhs);
emitDbgStmt(gz, saved_line, saved_col);
uint32_t result
= addPlNodeBin(gz, ZIR_INST_DIV_TRUNC, node, lhs, rhs);
return rvalue(gz, rl, result, node);
}
// @divExact — divBuiltin (AstGen.zig:9481, 9920-9936). // @divExact — divBuiltin (AstGen.zig:9481, 9920-9936).
if (name_len == 8 if (name_len == 8
&& memcmp(source + name_start, "divExact", 8) == 0) { && memcmp(source + name_start, "divExact", 8) == 0) {
@@ -3910,6 +4102,54 @@ static uint32_t builtinCallMultiArg(GenZir* gz, Scope* scope, ResultLoc rl,
return rvalue(gz, rl, result, node); return rvalue(gz, rl, result, node);
} }
// @call — AstGen.zig:9604-9619.
// clang-format off
if (name_len == 4 && memcmp(source + name_start, "call", 4) == 0
&& param_count == 3) {
uint32_t call_modifier_ty
= addBuiltinValue(gz, node, ZIR_BUILTIN_VALUE_CALL_MODIFIER);
ResultLoc mod_rl = { .tag = RL_COERCED_TY,
.data = call_modifier_ty, .src_node = 0, .ctx = RI_CTX_NONE };
uint32_t modifier
= comptimeExpr(gz, scope, mod_rl, params[0],
COMPTIME_REASON_CALL_MODIFIER);
uint32_t callee = expr(gz, scope, params[1]);
uint32_t args = expr(gz, scope, params[2]);
// BuiltinCall payload: flags, modifier, callee, args.
ensureExtraCapacity(ag, 4);
uint32_t payload_index = ag->extra_len;
ag->extra[ag->extra_len++] = 0; // flags: is_nosuspend=false, ensure_result_used=false
ag->extra[ag->extra_len++] = modifier;
ag->extra[ag->extra_len++] = callee;
ag->extra[ag->extra_len++] = args;
uint32_t result = addPlNodePayloadIndex(
gz, ZIR_INST_BUILTIN_CALL, node, payload_index);
return rvalue(gz, rl, result, node);
}
// clang-format on
// @shuffle — AstGen.zig:9655-9663.
// clang-format off
if (name_len == 7 && memcmp(source + name_start, "shuffle", 7) == 0
&& param_count == 4) {
uint32_t elem_type = typeExpr(gz, scope, params[0]);
uint32_t a = expr(gz, scope, params[1]);
uint32_t b = expr(gz, scope, params[2]);
uint32_t mask = comptimeExpr(gz, scope, RL_NONE_VAL, params[3],
COMPTIME_REASON_SHUFFLE_MASK);
// Shuffle payload: elem_type, a, b, mask (4 Refs).
ensureExtraCapacity(ag, 4);
uint32_t payload_index = ag->extra_len;
ag->extra[ag->extra_len++] = elem_type;
ag->extra[ag->extra_len++] = a;
ag->extra[ag->extra_len++] = b;
ag->extra[ag->extra_len++] = mask;
uint32_t result = addPlNodePayloadIndex(
gz, ZIR_INST_SHUFFLE, node, payload_index);
return rvalue(gz, rl, result, node);
}
// clang-format on
// TODO: handle other multi-arg builtins. // TODO: handle other multi-arg builtins.
SET_ERROR(ag); SET_ERROR(ag);
return ZIR_REF_VOID_VALUE; return ZIR_REF_VOID_VALUE;
@@ -12493,12 +12733,12 @@ static void fnDecl(AstGenCtx* ag, GenZir* gz, Scope* scope,
// --- Parameter iteration (AstGen.zig:4260-4363) --- // --- Parameter iteration (AstGen.zig:4260-4363) ---
// Walk params, creating param instructions and ScopeLocalVal entries. // Walk params, creating param instructions and ScopeLocalVal entries.
// We keep param scopes on the C stack (max 32 params like upstream). // We keep param scopes on the C stack.
Scope* params_scope = &decl_gz.base; Scope* params_scope = &decl_gz.base;
ScopeLocalVal param_scopes[32]; ScopeLocalVal param_scopes[256];
uint32_t param_scope_count = 0; uint32_t param_scope_count = 0;
// Collect param instruction indices (AstGen.zig:4254, 4360). // Collect param instruction indices (AstGen.zig:4254, 4360).
uint32_t param_insts[32]; uint32_t param_insts[256];
uint32_t param_insts_len = 0; uint32_t param_insts_len = 0;
// noalias_bits tracking (AstGen.zig:4259). // noalias_bits tracking (AstGen.zig:4259).
uint32_t noalias_bits = 0; uint32_t noalias_bits = 0;
@@ -12645,7 +12885,7 @@ static void fnDecl(AstGenCtx* ag, GenZir* gz, Scope* scope,
uint32_t anytype_inst = addStrTok( uint32_t anytype_inst = addStrTok(
&decl_gz, anytype_tag, param_name_str, anytype_name_token); &decl_gz, anytype_tag, param_name_str, anytype_name_token);
param_inst_ref = anytype_inst; // already a ref (toRef()) param_inst_ref = anytype_inst; // already a ref (toRef())
if (param_insts_len < 32) if (param_insts_len < 256)
param_insts[param_insts_len++] param_insts[param_insts_len++]
= anytype_inst - ZIR_REF_START_INDEX; // toIndex() = anytype_inst - ZIR_REF_START_INDEX; // toIndex()
} else { } else {
@@ -12675,12 +12915,12 @@ static void fnDecl(AstGenCtx* ag, GenZir* gz, Scope* scope,
name_tok_for_src, param_name_str, param_type_is_generic); name_tok_for_src, param_name_str, param_type_is_generic);
(void)param_inst_expected; (void)param_inst_expected;
param_inst_ref = param_inst + ZIR_REF_START_INDEX; param_inst_ref = param_inst + ZIR_REF_START_INDEX;
if (param_insts_len < 32) if (param_insts_len < 256)
param_insts[param_insts_len++] = param_inst; param_insts[param_insts_len++] = param_inst;
} }
// Create ScopeLocalVal for this param (AstGen.zig:4349-4359). // Create ScopeLocalVal for this param (AstGen.zig:4349-4359).
if (param_name_str != 0 && param_scope_count < 32) { if (param_name_str != 0 && param_scope_count < 256) {
ScopeLocalVal* lv = &param_scopes[param_scope_count++]; ScopeLocalVal* lv = &param_scopes[param_scope_count++];
lv->base.tag = SCOPE_LOCAL_VAL; lv->base.tag = SCOPE_LOCAL_VAL;
lv->parent = params_scope; lv->parent = params_scope;