feature. See also
. The project being documented here (as the example) is the Zig library itself.
AstGen.builtinCall
fn builtinCall(
gz: *GenZir,
scope: *Scope,
ri: ResultInfo,
node: Ast.Node.Index,
params: []const Ast.Node.Index,
allow_branch_hint: bool,
reify_name_strat: Zir.Inst.NameStrategy,
) InnerError!Zir.Inst.Ref
File
Code
fn builtinCall(
gz: *GenZir,
scope: *Scope,
ri: ResultInfo,
node: Ast.Node.Index,
params: []const Ast.Node.Index,
allow_branch_hint: bool,
reify_name_strat: Zir.Inst.NameStrategy,
) InnerError!Zir.Inst.Ref {
const astgen = gz.astgen;
const tree = astgen.tree;
const builtin_token = tree.nodeMainToken(node);
const builtin_name = tree.tokenSlice(builtin_token);
// on the function. For example, `@as` and others participate in result location semantics.
// Also, some builtins have a variable number of parameters.
const info = BuiltinFn.list.get(builtin_name) orelse {
return astgen.failNode(node, "invalid builtin function: '{s}'", .{
builtin_name,
});
};
if (info.param_count) |expected| {
if (expected != params.len) {
const s = if (expected == 1) "" else "s";
return astgen.failNode(node, "expected {d} argument{s}, found {d}", .{
expected, s, params.len,
});
}
}
if (astgen.fn_block == null and info.illegal_outside_function)
return astgen.failNode(node, "'{s}' outside function scope", .{builtin_name});
switch (info.tag) {
.branch_hint => {
if (!allow_branch_hint) {
return astgen.failNode(node, "'@branchHint' must appear as the first statement in a function or conditional branch", .{});
}
const hint_ty = try gz.addStdLangValue(node, .branch_hint);
const hint_val = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = hint_ty } }, params[0], .operand_branchHint);
_ = try gz.addExtendedPayload(.branch_hint, Zir.Inst.UnNode{
.node = gz.nodeIndexToRelative(node),
.operand = hint_val,
});
return rvalue(gz, ri, .void_value, node);
},
.import => {
const operand_node = params[0];
if (tree.nodeTag(operand_node) != .string_literal) {
return astgen.failNode(operand_node, "@import operand must be a string literal", .{});
}
const str_lit_token = tree.nodeMainToken(operand_node);
const str = try astgen.strLitAsString(str_lit_token);
const str_slice = astgen.string_bytes.items[@backingInt(str.index)..][0..str.len];
if (mem.findScalar(u8, str_slice, 0) != null) {
return astgen.failTok(str_lit_token, "import path cannot contain null bytes", .{});
} else if (str.len == 0) {
return astgen.failTok(str_lit_token, "import path cannot be empty", .{});
}
const res_ty = try ri.rl.resultType(gz, node) orelse .none;
const payload_index = try addExtra(gz.astgen, Zir.Inst.Import{
.res_ty = res_ty,
.path = str.index,
});
const result = try gz.add(.{
.tag = .import,
.data = .{ .pl_tok = .{
.src_tok = gz.tokenIndexToRelative(str_lit_token),
.payload_index = payload_index,
} },
});
const gop = try astgen.imports.getOrPut(astgen.gpa, str.index);
if (!gop.found_existing) {
gop.value_ptr.* = str_lit_token;
}
return rvalue(gz, ri, result, node);
},
.compile_log => {
const payload_index = try addExtra(gz.astgen, Zir.Inst.NodeMultiOp{
.src_node = gz.nodeIndexToRelative(node),
});
var extra_index = try reserveExtra(gz.astgen, params.len);
for (params) |param| {
const param_ref = try expr(gz, scope, .{ .rl = .none }, param);
astgen.extra.items[extra_index] = @backingInt(param_ref);
extra_index += 1;
}
const result = try gz.addExtendedMultiOpPayloadIndex(.compile_log, payload_index, params.len);
return rvalue(gz, ri, result, node);
},
.field => {
switch (ri.rl) {
.ref, .ref_coerced_ty => {
return gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{
.lhs = try expr(gz, scope, .{ .rl = .ref }, params[0]),
.field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1], .field_name),
});
},
.ref_const => {
return gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{
.lhs = try expr(gz, scope, .{ .rl = .ref_const }, params[0]),
.field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1], .field_name),
});
},
else => {
const result = try gz.addPlNode(.field_ptr_named_load, node, Zir.Inst.FieldNamed{
.lhs = try expr(gz, scope, .{ .rl = .ref_const }, params[0]),
.field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1], .field_name),
});
return rvalue(gz, ri, result, node);
},
}
},
.FieldType => {
const ty_inst = try typeExpr(gz, scope, params[0]);
const name_inst = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1], .field_name);
const result = try gz.addPlNode(.field_type_ref, node, Zir.Inst.FieldTypeRef{
.container_type = ty_inst,
.field_name = name_inst,
});
return rvalue(gz, ri, result, node);
},
.as => return as( gz, scope, ri, node, params[0], params[1]),
.bit_cast => return bitCast( gz, scope, ri, node, params[0]),
.TypeOf => return typeOf( gz, scope, ri, node, params),
.union_init => return unionInit(gz, scope, ri, node, params),
.min => return minMax( gz, scope, ri, node, params, .min),
.max => return minMax( gz, scope, ri, node, params, .max),
.@"export" => {
const exported = try expr(gz, scope, .{ .rl = .none }, params[0]);
const export_options_ty = try gz.addStdLangValue(node, .export_options);
const options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = export_options_ty } }, params[1], .export_options);
_ = try gz.addPlNode(.@"export", node, Zir.Inst.Export{
.exported = exported,
.options = options,
});
return rvalue(gz, ri, .void_value, node);
},
.@"extern" => {
const type_inst = try typeExpr(gz, scope, params[0]);
const extern_options_ty = try gz.addStdLangValue(node, .extern_options);
const options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = extern_options_ty } }, params[1], .extern_options);
const result = try gz.addExtendedPayload(.builtin_extern, Zir.Inst.BinNode{
.node = gz.nodeIndexToRelative(node),
.lhs = type_inst,
.rhs = options,
});
return rvalue(gz, ri, result, node);
},
.set_float_mode => {
const float_mode_ty = try gz.addStdLangValue(node, .float_mode);
const order = try expr(gz, scope, .{ .rl = .{ .coerced_ty = float_mode_ty } }, params[0]);
_ = try gz.addExtendedPayload(.set_float_mode, Zir.Inst.UnNode{
.node = gz.nodeIndexToRelative(node),
.operand = order,
});
return rvalue(gz, ri, .void_value, node);
},
.src => {
// changes in the source location of `@src()` result in re-analysis.
astgen.src_hasher.update(
std.mem.asBytes(&astgen.source_line) ++
std.mem.asBytes(&astgen.source_column),
);
const node_start = tree.tokenStart(tree.firstToken(node));
astgen.advanceSourceCursor(node_start);
const result = try gz.addExtendedPayload(.builtin_src, Zir.Inst.Src{
.node = gz.nodeIndexToRelative(node),
.line = astgen.source_line,
.column = astgen.source_column,
});
return rvalue(gz, ri, result, node);
},
.This => return rvalue(gz, ri, try gz.addNodeExtended(.this, node), node),
.return_address => return rvalue(gz, ri, try gz.addNodeExtended(.ret_addr, node), node),
.error_return_trace => return rvalue(gz, ri, try gz.addNodeExtended(.error_return_trace, node), node),
.frame => return rvalue(gz, ri, try gz.addNodeExtended(.frame, node), node),
.frame_address => return rvalue(gz, ri, try gz.addNodeExtended(.frame_address, node), node),
.breakpoint => return rvalue(gz, ri, try gz.addNodeExtended(.breakpoint, node), node),
.disable_instrumentation => return rvalue(gz, ri, try gz.addNodeExtended(.disable_instrumentation, node), node),
.disable_intrinsics => return rvalue(gz, ri, try gz.addNodeExtended(.disable_intrinsics, node), node),
.type_info => return simpleUnOpType(gz, scope, ri, node, params[0], .type_info),
.size_of => return simpleUnOpType(gz, scope, ri, node, params[0], .size_of),
.bit_size_of => return simpleUnOpType(gz, scope, ri, node, params[0], .bit_size_of),
.align_of => return simpleUnOpType(gz, scope, ri, node, params[0], .align_of),
.int_from_ptr => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .int_from_ptr),
.compile_error => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .compile_error),
.set_eval_branch_quota => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .set_eval_branch_quota),
.int_from_enum => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .int_from_enum),
.int_from_bool => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .int_from_bool),
.backing_int => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .backing_int),
.embed_file => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .embed_file),
.error_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .anyerror_type } }, params[0], .error_name),
.set_runtime_safety => return simpleUnOp(gz, scope, ri, node, coerced_bool_ri, params[0], .set_runtime_safety),
.abs => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .abs),
.tag_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .tag_name),
.type_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .type_name),
.Frame => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .frame_type),
.sqrt => return floatUnOp(gz, scope, ri, node, params[0], .sqrt),
.sin => return floatUnOp(gz, scope, ri, node, params[0], .sin),
.cos => return floatUnOp(gz, scope, ri, node, params[0], .cos),
.tan => return floatUnOp(gz, scope, ri, node, params[0], .tan),
.exp => return floatUnOp(gz, scope, ri, node, params[0], .exp),
.exp2 => return floatUnOp(gz, scope, ri, node, params[0], .exp2),
.log => return floatUnOp(gz, scope, ri, node, params[0], .log),
.log2 => return floatUnOp(gz, scope, ri, node, params[0], .log2),
.log10 => return floatUnOp(gz, scope, ri, node, params[0], .log10),
.floor => return floatRoundOp(gz, scope, ri, node, params[0], .floor),
.ceil => return floatRoundOp(gz, scope, ri, node, params[0], .ceil),
.trunc => return floatRoundOp(gz, scope, ri, node, params[0], .trunc),
.round => return floatRoundOp(gz, scope, ri, node, params[0], .round),
.int_from_float => return typeCast(gz, scope, ri, node, params[0], .int_from_float, builtin_name),
.float_from_int => return typeCast(gz, scope, ri, node, params[0], .float_from_int, builtin_name),
.ptr_from_int => return typeCast(gz, scope, ri, node, params[0], .ptr_from_int, builtin_name),
.enum_from_int => return typeCast(gz, scope, ri, node, params[0], .enum_from_int, builtin_name),
.float_cast => return typeCast(gz, scope, ri, node, params[0], .float_cast, builtin_name),
.int_cast => return typeCast(gz, scope, ri, node, params[0], .int_cast, builtin_name),
.truncate => return typeCast(gz, scope, ri, node, params[0], .truncate, builtin_name),
.from_backing_int => {
const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
const result_ty = try ri.rl.resultTypeForCast(gz, node, builtin_name);
const backing_int_ty = try gz.addUnNode(.from_backing_int_arg_ty, result_ty, node);
const operand = try expr(gz, scope, .{ .rl = .{ .coerced_ty = backing_int_ty } }, params[0]);
try emitDbgStmt(gz, cursor);
const result = try gz.addPlNode(.from_backing_int, node, Zir.Inst.Bin{
.lhs = result_ty,
.rhs = operand,
});
return rvalue(gz, ri, result, node);
},
.in_comptime => if (gz.is_comptime) {
return astgen.failNode(node, "redundant '@inComptime' in comptime scope", .{});
} else {
return rvalue(gz, ri, try gz.addNodeExtended(.in_comptime, node), node);
},
.EnumLiteral => return rvalue(gz, ri, .enum_literal_type, node),
.Int => {
const signedness_ty = try gz.addStdLangValue(node, .signedness);
const result = try gz.addPlNode(.reify_int, node, Zir.Inst.Bin{
.lhs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = signedness_ty } }, params[0], .int_signedness),
.rhs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u16_type } }, params[1], .int_bit_width),
});
return rvalue(gz, ri, result, node);
},
.Tuple => {
const result = try gz.addExtendedPayload(.reify_tuple, Zir.Inst.UnNode{
.node = gz.nodeIndexToRelative(node),
.operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_type_type } }, params[0], .tuple_field_types),
});
return rvalue(gz, ri, result, node);
},
.Pointer => {
const ptr_size_ty = try gz.addStdLangValue(node, .pointer_size);
const ptr_attrs_ty = try gz.addStdLangValue(node, .pointer_attributes);
const size = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = ptr_size_ty } }, params[0], .pointer_size);
const attrs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = ptr_attrs_ty } }, params[1], .pointer_attrs);
const elem_ty = try typeExpr(gz, scope, params[2]);
const sentinel_ty = try gz.addExtendedPayload(.reify_pointer_sentinel_ty, Zir.Inst.UnNode{
.node = gz.nodeIndexToRelative(params[2]),
.operand = elem_ty,
});
const sentinel = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = sentinel_ty } }, params[3], .pointer_sentinel);
const result = try gz.addExtendedPayload(.reify_pointer, Zir.Inst.ReifyPointer{
.node = gz.nodeIndexToRelative(node),
.size = size,
.attrs = attrs,
.elem_ty = elem_ty,
.sentinel = sentinel,
});
return rvalue(gz, ri, result, node);
},
.Fn => {
const fn_attrs_ty = try gz.addStdLangValue(node, .fn_attributes);
const param_types = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_type_type } }, params[0], .fn_param_types);
const param_attrs_ty = try gz.addExtendedPayloadSmall(
.reify_slice_arg_ty,
@backingInt(Zir.Inst.ReifySliceArgInfo.type_to_fn_param_attrs),
Zir.Inst.UnNode{ .node = gz.nodeIndexToRelative(params[0]), .operand = param_types },
);
const param_attrs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = param_attrs_ty } }, params[1], .fn_param_attrs);
const ret_ty = try comptimeExpr(gz, scope, coerced_type_ri, params[2], .fn_ret_ty);
const fn_attrs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = fn_attrs_ty } }, params[3], .fn_attrs);
const result = try gz.addExtendedPayload(.reify_fn, Zir.Inst.ReifyFn{
.node = gz.nodeIndexToRelative(node),
.param_types = param_types,
.param_attrs = param_attrs,
.ret_ty = ret_ty,
.fn_attrs = fn_attrs,
});
return rvalue(gz, ri, result, node);
},
.Struct => {
const container_layout_ty = try gz.addStdLangValue(node, .container_layout);
const layout = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = container_layout_ty } }, params[0], .struct_layout);
const backing_ty = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .optional_type_type } }, params[1], .type);
const field_names = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_slice_const_u8_type } }, params[2], .struct_field_names);
const field_types_ty = try gz.addExtendedPayloadSmall(
.reify_slice_arg_ty,
@backingInt(Zir.Inst.ReifySliceArgInfo.string_to_struct_field_type),
Zir.Inst.UnNode{ .node = gz.nodeIndexToRelative(params[2]), .operand = field_names },
);
const field_attrs_ty = try gz.addExtendedPayloadSmall(
.reify_slice_arg_ty,
@backingInt(Zir.Inst.ReifySliceArgInfo.string_to_struct_field_attrs),
Zir.Inst.UnNode{ .node = gz.nodeIndexToRelative(params[2]), .operand = field_names },
);
const field_types = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = field_types_ty } }, params[3], .struct_field_types);
const field_attrs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = field_attrs_ty } }, params[4], .struct_field_attrs);
const result = try gz.addExtendedPayloadSmall(.reify_struct, @backingInt(reify_name_strat), Zir.Inst.ReifyStruct{
.src_line = gz.astgen.source_line,
.node = node,
.layout = layout,
.backing_ty = backing_ty,
.field_names = field_names,
.field_types = field_types,
.field_attrs = field_attrs,
});
return rvalue(gz, ri, result, node);
},
.Union => {
const container_layout_ty = try gz.addStdLangValue(node, .container_layout);
const layout = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = container_layout_ty } }, params[0], .union_layout);
const arg_ty = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .optional_type_type } }, params[1], .type);
const field_names = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_slice_const_u8_type } }, params[2], .union_field_names);
const field_types_ty = try gz.addExtendedPayloadSmall(
.reify_slice_arg_ty,
@backingInt(Zir.Inst.ReifySliceArgInfo.string_to_union_field_type),
Zir.Inst.UnNode{ .node = gz.nodeIndexToRelative(params[2]), .operand = field_names },
);
const field_attrs_ty = try gz.addExtendedPayloadSmall(
.reify_slice_arg_ty,
@backingInt(Zir.Inst.ReifySliceArgInfo.string_to_union_field_attrs),
Zir.Inst.UnNode{ .node = gz.nodeIndexToRelative(params[2]), .operand = field_names },
);
const field_types = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = field_types_ty } }, params[3], .union_field_types);
const field_attrs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = field_attrs_ty } }, params[4], .union_field_attrs);
const result = try gz.addExtendedPayloadSmall(.reify_union, @backingInt(reify_name_strat), Zir.Inst.ReifyUnion{
.src_line = gz.astgen.source_line,
.node = node,
.layout = layout,
.arg_ty = arg_ty,
.field_names = field_names,
.field_types = field_types,
.field_attrs = field_attrs,
});
return rvalue(gz, ri, result, node);
},
.Enum => {
const enum_mode_ty = try gz.addStdLangValue(node, .enum_mode);
const tag_ty = try typeExpr(gz, scope, params[0]);
const mode = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = enum_mode_ty } }, params[1], .type);
const field_names = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_slice_const_u8_type } }, params[2], .enum_field_names);
const field_values_ty = try gz.addExtendedPayload(.reify_enum_value_slice_ty, Zir.Inst.BinNode{
.node = gz.nodeIndexToRelative(node),
.lhs = tag_ty,
.rhs = field_names,
});
const field_values = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = field_values_ty } }, params[3], .enum_field_values);
const result = try gz.addExtendedPayloadSmall(.reify_enum, @backingInt(reify_name_strat), Zir.Inst.ReifyEnum{
.src_line = gz.astgen.source_line,
.node = node,
.tag_ty = tag_ty,
.mode = mode,
.field_names = field_names,
.field_values = field_values,
});
return rvalue(gz, ri, result, node);
},
.SpirvType => {
const spirv_type_options_ty = try gz.addStdLangValue(node, .spirv_type_options);
const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = spirv_type_options_ty } }, params[0], .type);
const result = try gz.addExtendedPayload(.reify_spirv_type, Zir.Inst.ReifySpirvType{
.src_line = gz.astgen.source_line,
.node = node,
.operand = operand,
});
return rvalue(gz, ri, result, node);
},
.panic => {
try emitDbgNode(gz, node);
return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .panic);
},
.trap => {
try emitDbgNode(gz, node);
_ = try gz.addNode(.trap, node);
return rvalue(gz, ri, .unreachable_value, node);
},
.int_from_error => {
const operand = try expr(gz, scope, .{ .rl = .none }, params[0]);
const result = try gz.addExtendedPayload(.int_from_error, Zir.Inst.UnNode{
.node = gz.nodeIndexToRelative(node),
.operand = operand,
});
return rvalue(gz, ri, result, node);
},
.error_from_int => {
const operand = try expr(gz, scope, .{ .rl = .none }, params[0]);
const result = try gz.addExtendedPayload(.error_from_int, Zir.Inst.UnNode{
.node = gz.nodeIndexToRelative(node),
.operand = operand,
});
return rvalue(gz, ri, result, node);
},
.error_cast => {
try emitDbgNode(gz, node);
const result = try gz.addExtendedPayload(.error_cast, Zir.Inst.BinNode{
.lhs = try ri.rl.resultTypeForCast(gz, node, builtin_name),
.rhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
.node = gz.nodeIndexToRelative(node),
});
return rvalue(gz, ri, result, node);
},
.ptr_cast,
.align_cast,
.addrspace_cast,
.const_cast,
.volatile_cast,
=> return ptrCast(gz, scope, ri, node),
.has_decl => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_decl),
.has_field => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_field),
.clz => return bitBuiltin(gz, scope, ri, node, params[0], .clz),
.ctz => return bitBuiltin(gz, scope, ri, node, params[0], .ctz),
.pop_count => return bitBuiltin(gz, scope, ri, node, params[0], .pop_count),
.byte_swap => return bitBuiltin(gz, scope, ri, node, params[0], .byte_swap),
.bit_reverse => return bitBuiltin(gz, scope, ri, node, params[0], .bit_reverse),
.div_exact => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_exact),
.div_floor => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_floor),
.div_ceil => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_ceil),
.div_trunc => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_trunc),
.mod => return divBuiltin(gz, scope, ri, node, params[0], params[1], .mod),
.rem => return divBuiltin(gz, scope, ri, node, params[0], params[1], .rem),
.shl_exact => return shiftOp(gz, scope, ri, node, params[0], params[1], .shl_exact),
.shr_exact => return shiftOp(gz, scope, ri, node, params[0], params[1], .shr_exact),
.bit_offset_of => return offsetOf(gz, scope, ri, node, params[0], params[1], .bit_offset_of),
.offset_of => return offsetOf(gz, scope, ri, node, params[0], params[1], .offset_of),
.cmpxchg_strong => return cmpxchg(gz, scope, ri, node, params, 1),
.cmpxchg_weak => return cmpxchg(gz, scope, ri, node, params, 0),
.wasm_memory_size => {
const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .wasm_memory_index);
const result = try gz.addExtendedPayload(.wasm_memory_size, Zir.Inst.UnNode{
.node = gz.nodeIndexToRelative(node),
.operand = operand,
});
return rvalue(gz, ri, result, node);
},
.wasm_memory_grow => {
const index_arg = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .wasm_memory_index);
const delta_arg = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, params[1]);
const result = try gz.addExtendedPayload(.wasm_memory_grow, Zir.Inst.BinNode{
.node = gz.nodeIndexToRelative(node),
.lhs = index_arg,
.rhs = delta_arg,
});
return rvalue(gz, ri, result, node);
},
.splat => {
const result_type = try ri.rl.resultTypeForCast(gz, node, builtin_name);
const elem_type = try gz.addUnNode(.splat_op_result_ty, result_type, node);
const scalar = try expr(gz, scope, .{ .rl = .{ .ty = elem_type } }, params[0]);
const result = try gz.addPlNode(.splat, node, Zir.Inst.Bin{
.lhs = result_type,
.rhs = scalar,
});
return rvalue(gz, ri, result, node);
},
.reduce => {
const reduce_op_ty = try gz.addStdLangValue(node, .reduce_op);
const op = try expr(gz, scope, .{ .rl = .{ .coerced_ty = reduce_op_ty } }, params[0]);
const scalar = try expr(gz, scope, .{ .rl = .none }, params[1]);
const result = try gz.addPlNode(.reduce, node, Zir.Inst.Bin{
.lhs = op,
.rhs = scalar,
});
return rvalue(gz, ri, result, node);
},
.add_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .add_with_overflow),
.sub_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .sub_with_overflow),
.mul_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .mul_with_overflow),
.shl_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .shl_with_overflow),
.atomic_load => {
const atomic_order_type = try gz.addStdLangValue(node, .atomic_order);
const result = try gz.addPlNode(.atomic_load, node, Zir.Inst.AtomicLoad{
.elem_type = try typeExpr(gz, scope, params[0]),
.ptr = try expr (gz, scope, .{ .rl = .none }, params[1]),
.ordering = try expr (gz, scope, .{ .rl = .{ .coerced_ty = atomic_order_type } }, params[2]),
});
return rvalue(gz, ri, result, node);
},
.atomic_rmw => {
const atomic_order_type = try gz.addStdLangValue(node, .atomic_order);
const atomic_rmw_op_type = try gz.addStdLangValue(node, .atomic_rmw_op);
const int_type = try typeExpr(gz, scope, params[0]);
const result = try gz.addPlNode(.atomic_rmw, node, Zir.Inst.AtomicRmw{
.ptr = try expr(gz, scope, .{ .rl = .none }, params[1]),
.operation = try expr(gz, scope, .{ .rl = .{ .coerced_ty = atomic_rmw_op_type } }, params[2]),
.operand = try expr(gz, scope, .{ .rl = .{ .ty = int_type } }, params[3]),
.ordering = try expr(gz, scope, .{ .rl = .{ .coerced_ty = atomic_order_type } }, params[4]),
});
return rvalue(gz, ri, result, node);
},
.atomic_store => {
const atomic_order_type = try gz.addStdLangValue(node, .atomic_order);
const int_type = try typeExpr(gz, scope, params[0]);
_ = try gz.addPlNode(.atomic_store, node, Zir.Inst.AtomicStore{
.ptr = try expr(gz, scope, .{ .rl = .none }, params[1]),
.operand = try expr(gz, scope, .{ .rl = .{ .ty = int_type } }, params[2]),
.ordering = try expr(gz, scope, .{ .rl = .{ .coerced_ty = atomic_order_type } }, params[3]),
});
return rvalue(gz, ri, .void_value, node);
},
.mul_add => {
const float_type = try typeExpr(gz, scope, params[0]);
const mulend1 = try expr(gz, scope, .{ .rl = .{ .coerced_ty = float_type } }, params[1]);
const mulend2 = try expr(gz, scope, .{ .rl = .{ .coerced_ty = float_type } }, params[2]);
const addend = try expr(gz, scope, .{ .rl = .{ .ty = float_type } }, params[3]);
const result = try gz.addPlNode(.mul_add, node, Zir.Inst.MulAdd{
.mulend1 = mulend1,
.mulend2 = mulend2,
.addend = addend,
});
return rvalue(gz, ri, result, node);
},
.call => {
const call_modifier_ty = try gz.addStdLangValue(node, .call_modifier);
const modifier = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = call_modifier_ty } }, params[0], .call_modifier);
const callee = try expr(gz, scope, .{ .rl = .none }, params[1]);
const args = try expr(gz, scope, .{ .rl = .none }, params[2]);
const result = try gz.addPlNode(.builtin_call, node, Zir.Inst.BuiltinCall{
.modifier = modifier,
.callee = callee,
.args = args,
.flags = .{
.is_nosuspend = gz.nosuspend_node != .none,
.ensure_result_used = false,
},
});
return rvalue(gz, ri, result, node);
},
.field_parent_ptr => {
const parent_ptr_type = try ri.rl.resultTypeForCast(gz, node, builtin_name);
const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .field_name);
const result = try gz.addExtendedPayloadSmall(.field_parent_ptr, 0, Zir.Inst.FieldParentPtr{
.src_node = gz.nodeIndexToRelative(node),
.parent_ptr_type = parent_ptr_type,
.field_name = field_name,
.field_ptr = try expr(gz, scope, .{ .rl = .none }, params[1]),
});
return rvalue(gz, ri, result, node);
},
.memcpy => {
_ = try gz.addPlNode(.memcpy, node, Zir.Inst.Bin{
.lhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
.rhs = try expr(gz, scope, .{ .rl = .none }, params[1]),
});
return rvalue(gz, ri, .void_value, node);
},
.memset => {
const lhs = try expr(gz, scope, .{ .rl = .none }, params[0]);
const lhs_ty = try gz.addUnNode(.typeof, lhs, params[0]);
const elem_ty = try gz.addUnNode(.indexable_ptr_elem_type, lhs_ty, params[0]);
_ = try gz.addPlNode(.memset, node, Zir.Inst.Bin{
.lhs = lhs,
.rhs = try expr(gz, scope, .{ .rl = .{ .coerced_ty = elem_ty } }, params[1]),
});
return rvalue(gz, ri, .void_value, node);
},
.memmove => {
_ = try gz.addPlNode(.memmove, node, Zir.Inst.Bin{
.lhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
.rhs = try expr(gz, scope, .{ .rl = .none }, params[1]),
});
return rvalue(gz, ri, .void_value, node);
},
.shuffle => {
const result = try gz.addPlNode(.shuffle, node, Zir.Inst.Shuffle{
.elem_type = try typeExpr(gz, scope, params[0]),
.a = try expr(gz, scope, .{ .rl = .none }, params[1]),
.b = try expr(gz, scope, .{ .rl = .none }, params[2]),
.mask = try comptimeExpr(gz, scope, .{ .rl = .none }, params[3], .operand_shuffle_mask),
});
return rvalue(gz, ri, result, node);
},
.select => {
const result = try gz.addExtendedPayload(.select, Zir.Inst.Select{
.node = gz.nodeIndexToRelative(node),
.elem_type = try typeExpr(gz, scope, params[0]),
.pred = try expr(gz, scope, .{ .rl = .none }, params[1]),
.a = try expr(gz, scope, .{ .rl = .none }, params[2]),
.b = try expr(gz, scope, .{ .rl = .none }, params[3]),
});
return rvalue(gz, ri, result, node);
},
.Vector => {
const result = try gz.addPlNode(.vector_type, node, Zir.Inst.Bin{
.lhs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .type),
.rhs = try typeExpr(gz, scope, params[1]),
});
return rvalue(gz, ri, result, node);
},
.prefetch => {
const prefetch_options_ty = try gz.addStdLangValue(node, .prefetch_options);
const ptr = try expr(gz, scope, .{ .rl = .none }, params[0]);
const options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = prefetch_options_ty } }, params[1], .prefetch_options);
_ = try gz.addExtendedPayload(.prefetch, Zir.Inst.BinNode{
.node = gz.nodeIndexToRelative(node),
.lhs = ptr,
.rhs = options,
});
return rvalue(gz, ri, .void_value, node);
},
.c_va_arg => {
const result = try gz.addExtendedPayload(.c_va_arg, Zir.Inst.BinNode{
.node = gz.nodeIndexToRelative(node),
.lhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
.rhs = try typeExpr(gz, scope, params[1]),
});
return rvalue(gz, ri, result, node);
},
.c_va_copy => {
const result = try gz.addExtendedPayload(.c_va_copy, Zir.Inst.UnNode{
.node = gz.nodeIndexToRelative(node),
.operand = try expr(gz, scope, .{ .rl = .none }, params[0]),
});
return rvalue(gz, ri, result, node);
},
.c_va_end => {
const result = try gz.addExtendedPayload(.c_va_end, Zir.Inst.UnNode{
.node = gz.nodeIndexToRelative(node),
.operand = try expr(gz, scope, .{ .rl = .none }, params[0]),
});
return rvalue(gz, ri, result, node);
},
.c_va_start => {
if (!astgen.fn_var_args) {
return astgen.failNode(node, "'@cVaStart' in a non-variadic function", .{});
}
return rvalue(gz, ri, try gz.addNodeExtended(.c_va_start, node), node);
},
.work_item_id => {
const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .work_group_dim_index);
const result = try gz.addExtendedPayload(.work_item_id, Zir.Inst.UnNode{
.node = gz.nodeIndexToRelative(node),
.operand = operand,
});
return rvalue(gz, ri, result, node);
},
.work_group_size => {
const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .work_group_dim_index);
const result = try gz.addExtendedPayload(.work_group_size, Zir.Inst.UnNode{
.node = gz.nodeIndexToRelative(node),
.operand = operand,
});
return rvalue(gz, ri, result, node);
},
.work_group_id => {
const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .work_group_dim_index);
const result = try gz.addExtendedPayload(.work_group_id, Zir.Inst.UnNode{
.node = gz.nodeIndexToRelative(node),
.operand = operand,
});
return rvalue(gz, ri, result, node);
},
}
}