aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVeikka Tuominen <git@vexu.eu>2022-12-13 00:14:54 +0200
committerVeikka Tuominen <git@vexu.eu>2022-12-17 13:22:09 +0200
commit9bb1104e373dec192fb2a22d48b023330ddbaeae (patch)
tree0105b65b2a6e405ee13f2c8f3719c81d5de1b2ad /src
parent728dd29f1ac4e75111fec0299e50cf94c6a78760 (diff)
downloadzig-9bb1104e373dec192fb2a22d48b023330ddbaeae.tar.gz
zig-9bb1104e373dec192fb2a22d48b023330ddbaeae.zip
implement defining C variadic functions
Diffstat (limited to 'src')
-rw-r--r--src/Air.zig17
-rw-r--r--src/AstGen.zig49
-rw-r--r--src/BuiltinFn.zig28
-rw-r--r--src/Liveness.zig8
-rw-r--r--src/Sema.zig97
-rw-r--r--src/Zir.zig12
-rw-r--r--src/arch/aarch64/CodeGen.zig5
-rw-r--r--src/arch/arm/CodeGen.zig5
-rw-r--r--src/arch/riscv64/CodeGen.zig5
-rw-r--r--src/arch/sparc64/CodeGen.zig5
-rw-r--r--src/arch/wasm/CodeGen.zig4
-rw-r--r--src/arch/x86_64/CodeGen.zig5
-rw-r--r--src/codegen/c.zig5
-rw-r--r--src/codegen/llvm.zig93
-rw-r--r--src/codegen/llvm/bindings.zig3
-rw-r--r--src/print_air.zig4
-rw-r--r--src/print_zir.zig4
17 files changed, 344 insertions, 5 deletions
diff --git a/src/Air.zig b/src/Air.zig
index 707e700f08..4a7d03d7b5 100644
--- a/src/Air.zig
+++ b/src/Air.zig
@@ -741,6 +741,19 @@ pub const Inst = struct {
/// Uses the `vector_store_elem` field.
vector_store_elem,
+ /// Implements @cVaArg builtin.
+ /// Uses the `ty_op` field.
+ c_va_arg,
+ /// Implements @cVaCopy builtin.
+ /// Uses the `ty_op` field.
+ c_va_copy,
+ /// Implements @cVaEnd builtin.
+ /// Uses the `un_op` field.
+ c_va_end,
+ /// Implements @cVaStart builtin.
+ /// Uses the `ty` field.
+ c_va_start,
+
pub fn fromCmpOp(op: std.math.CompareOperator, optimized: bool) Tag {
switch (op) {
.lt => return if (optimized) .cmp_lt_optimized else .cmp_lt,
@@ -1092,6 +1105,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
.ret_ptr,
.arg,
.err_return_trace,
+ .c_va_start,
=> return datas[inst].ty,
.assembly,
@@ -1156,6 +1170,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
.byte_swap,
.bit_reverse,
.addrspace_cast,
+ .c_va_arg,
+ .c_va_copy,
=> return air.getRefType(datas[inst].ty_op.ty),
.loop,
@@ -1187,6 +1203,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
.prefetch,
.set_err_return_trace,
.vector_store_elem,
+ .c_va_end,
=> return Type.void,
.ptrtoint,
diff --git a/src/AstGen.zig b/src/AstGen.zig
index 69f3e2d381..4afc9a5454 100644
--- a/src/AstGen.zig
+++ b/src/AstGen.zig
@@ -42,6 +42,7 @@ string_table: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.d
compile_errors: ArrayListUnmanaged(Zir.Inst.CompileErrors.Item) = .{},
/// The topmost block of the current function.
fn_block: ?*GenZir = null,
+fn_var_args: bool = false,
/// Maps string table indexes to the first `@import` ZIR instruction
/// that uses this string as the operand.
imports: std.AutoArrayHashMapUnmanaged(u32, Ast.TokenIndex) = .{},
@@ -3892,10 +3893,6 @@ fn fnDecl(
.noalias_bits = noalias_bits,
});
} else func: {
- if (is_var_args) {
- return astgen.failTok(fn_proto.ast.fn_token, "non-extern function is variadic", .{});
- }
-
// as a scope, fn_gz encloses ret_gz, but for instruction list, fn_gz stacks on ret_gz
fn_gz.instructions_top = ret_gz.instructions.items.len;
@@ -3903,6 +3900,10 @@ fn fnDecl(
astgen.fn_block = &fn_gz;
defer astgen.fn_block = prev_fn_block;
+ const prev_var_args = astgen.fn_var_args;
+ astgen.fn_var_args = is_var_args;
+ defer astgen.fn_var_args = prev_var_args;
+
astgen.advanceSourceCursorToNode(body_node);
const lbrace_line = astgen.source_line - decl_gz.decl_line;
const lbrace_column = astgen.source_column;
@@ -8384,6 +8385,46 @@ fn builtinCall(
});
return rvalue(gz, ri, result, node);
},
+ .c_va_arg => {
+ if (astgen.fn_block == null) {
+ return astgen.failNode(node, "'@cVaArg' outside function scope", .{});
+ }
+ 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 => {
+ if (astgen.fn_block == null) {
+ return astgen.failNode(node, "'@cVaCopy' outside function scope", .{});
+ }
+ 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 => {
+ if (astgen.fn_block == null) {
+ return astgen.failNode(node, "'@cVaEnd' outside function scope", .{});
+ }
+ 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_block == null) {
+ return astgen.failNode(node, "'@cVaStart' outside function scope", .{});
+ }
+ 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);
+ },
}
}
diff --git a/src/BuiltinFn.zig b/src/BuiltinFn.zig
index 24625dc10a..7e23be2a3a 100644
--- a/src/BuiltinFn.zig
+++ b/src/BuiltinFn.zig
@@ -30,6 +30,10 @@ pub const Tag = enum {
compile_log,
ctz,
c_undef,
+ c_va_arg,
+ c_va_copy,
+ c_va_end,
+ c_va_start,
div_exact,
div_floor,
div_trunc,
@@ -355,6 +359,30 @@ pub const list = list: {
},
},
.{
+ "@cVaArg", .{
+ .tag = .c_va_arg,
+ .param_count = 2,
+ },
+ },
+ .{
+ "@cVaCopy", .{
+ .tag = .c_va_copy,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@cVaEnd", .{
+ .tag = .c_va_end,
+ .param_count = 1,
+ },
+ },
+ .{
+ "@cVaStart", .{
+ .tag = .c_va_start,
+ .param_count = 0,
+ },
+ },
+ .{
"@divExact",
.{
.tag = .div_exact,
diff --git a/src/Liveness.zig b/src/Liveness.zig
index d129dc01bf..b5b5cd810f 100644
--- a/src/Liveness.zig
+++ b/src/Liveness.zig
@@ -238,6 +238,7 @@ pub fn categorizeOperand(
.wasm_memory_size,
.err_return_trace,
.save_err_return_trace_index,
+ .c_va_start,
=> return .none,
.fence => return .write,
@@ -279,6 +280,8 @@ pub fn categorizeOperand(
.splat,
.error_set_has_value,
.addrspace_cast,
+ .c_va_arg,
+ .c_va_copy,
=> {
const o = air_datas[inst].ty_op;
if (o.operand == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none);
@@ -322,6 +325,7 @@ pub fn categorizeOperand(
.trunc_float,
.neg,
.cmp_lt_errors_len,
+ .c_va_end,
=> {
const o = air_datas[inst].un_op;
if (o == operand_ref) return matchOperandSmallIndex(l, inst, 0, .none);
@@ -857,6 +861,7 @@ fn analyzeInst(
.wasm_memory_size,
.err_return_trace,
.save_err_return_trace_index,
+ .c_va_start,
=> return trackOperands(a, new_set, inst, main_tomb, .{ .none, .none, .none }),
.not,
@@ -898,6 +903,8 @@ fn analyzeInst(
.splat,
.error_set_has_value,
.addrspace_cast,
+ .c_va_arg,
+ .c_va_copy,
=> {
const o = inst_datas[inst].ty_op;
return trackOperands(a, new_set, inst, main_tomb, .{ o.operand, .none, .none });
@@ -936,6 +943,7 @@ fn analyzeInst(
.neg_optimized,
.cmp_lt_errors_len,
.set_err_return_trace,
+ .c_va_end,
=> {
const operand = inst_datas[inst].un_op;
return trackOperands(a, new_set, inst, main_tomb, .{ operand, .none, .none });
diff --git a/src/Sema.zig b/src/Sema.zig
index 0a7496f730..0d065330b4 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -1148,6 +1148,10 @@ fn analyzeBodyInner(
.builtin_async_call => try sema.zirBuiltinAsyncCall( block, extended),
.cmpxchg => try sema.zirCmpxchg( block, extended),
.addrspace_cast => try sema.zirAddrSpaceCast( block, extended),
+ .c_va_arg => try sema.zirCVaArg( block, extended),
+ .c_va_copy => try sema.zirCVaCopy( block, extended),
+ .c_va_end => try sema.zirCVaEnd( block, extended),
+ .c_va_start => try sema.zirCVaStart( block, extended),
// zig fmt: on
.fence => {
@@ -6427,6 +6431,11 @@ fn analyzeCall(
else => unreachable,
};
if (!is_comptime_call and module_fn.state == .sema_failure) return error.AnalysisFail;
+ if (func_ty_info.is_var_args) {
+ return sema.fail(block, call_src, "{s} call of variadic function", .{
+ @as([]const u8, if (is_comptime_call) "comptime" else "inline"),
+ });
+ }
// Analyze the ZIR. The same ZIR gets analyzed into a runtime function
// or an inlined call depending on what union tag the `label` field is
@@ -8404,6 +8413,7 @@ fn funcCommon(
) CompileError!Air.Inst.Ref {
const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset };
const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = src_node_offset };
+ const func_src = LazySrcLoc.nodeOffset(src_node_offset);
var is_generic = bare_return_type.tag() == .generic_poison or
alignment == null or
@@ -8411,6 +8421,15 @@ fn funcCommon(
section == .generic or
cc == null;
+ if (var_args) {
+ if (is_generic) {
+ return sema.fail(block, func_src, "generic function cannot be variadic", .{});
+ }
+ if (cc.? != .C) {
+ return sema.fail(block, cc_src, "variadic function must have 'C' calling convention", .{});
+ }
+ }
+
var destroy_fn_on_error = false;
const new_func: *Module.Fn = new_func: {
if (!has_body) break :new_func undefined;
@@ -19054,6 +19073,79 @@ fn zirAddrSpaceCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Inst
});
}
+fn resolveVaListRef(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) CompileError!Air.Inst.Ref {
+ const va_list_ty = try sema.getBuiltinType("VaList");
+ const va_list_ptr = try Type.ptr(sema.arena, sema.mod, .{
+ .pointee_type = va_list_ty,
+ .mutable = true,
+ .@"addrspace" = .generic,
+ });
+
+ const inst = try sema.resolveInst(zir_ref);
+ return sema.coerce(block, va_list_ptr, inst, src);
+}
+
+fn zirCVaArg(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
+ const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
+ const src = LazySrcLoc.nodeOffset(extra.node);
+ const va_list_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
+ const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
+
+ const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.lhs);
+ const arg_ty = try sema.resolveType(block, ty_src, extra.rhs);
+
+ if (!try sema.validateExternType(arg_ty, .param_ty)) {
+ const msg = msg: {
+ const msg = try sema.errMsg(block, ty_src, "cannot get '{}' from variadic argument", .{arg_ty.fmt(sema.mod)});
+ errdefer msg.destroy(sema.gpa);
+
+ const src_decl = sema.mod.declPtr(block.src_decl);
+ try sema.explainWhyTypeIsNotExtern(msg, ty_src.toSrcLoc(src_decl), arg_ty, .param_ty);
+
+ try sema.addDeclaredHereNote(msg, arg_ty);
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(msg);
+ }
+
+ try sema.requireRuntimeBlock(block, src, null);
+ return block.addTyOp(.c_va_arg, arg_ty, va_list_ref);
+}
+
+fn zirCVaCopy(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
+ const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
+ const src = LazySrcLoc.nodeOffset(extra.node);
+ const va_list_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
+
+ const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.operand);
+ const va_list_ty = try sema.getBuiltinType("VaList");
+
+ try sema.requireRuntimeBlock(block, src, null);
+ return block.addTyOp(.c_va_copy, va_list_ty, va_list_ref);
+}
+
+fn zirCVaEnd(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
+ const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
+ const src = LazySrcLoc.nodeOffset(extra.node);
+ const va_list_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
+
+ const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.operand);
+
+ try sema.requireRuntimeBlock(block, src, null);
+ return block.addUnOp(.c_va_end, va_list_ref);
+}
+
+fn zirCVaStart(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
+ const src = LazySrcLoc.nodeOffset(@bitCast(i32, extended.operand));
+
+ const va_list_ty = try sema.getBuiltinType("VaList");
+ try sema.requireRuntimeBlock(block, src, null);
+ return block.addInst(.{
+ .tag = .c_va_start,
+ .data = .{ .ty = va_list_ty },
+ });
+}
+
fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
@@ -21558,7 +21650,10 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
else => |e| return e,
};
break :blk cc_tv.val.toEnum(std.builtin.CallingConvention);
- } else std.builtin.CallingConvention.Unspecified;
+ } else if (sema.owner_decl.is_exported and has_body)
+ .C
+ else
+ .Unspecified;
const ret_ty: Type = if (extra.data.bits.has_ret_ty_body) blk: {
const body_len = sema.code.extra[extra_index];
diff --git a/src/Zir.zig b/src/Zir.zig
index 6e7164878c..cadb5c6b44 100644
--- a/src/Zir.zig
+++ b/src/Zir.zig
@@ -1993,6 +1993,18 @@ pub const Inst = struct {
/// Implement the builtin `@addrSpaceCast`
/// `Operand` is payload index to `BinNode`. `lhs` is dest type, `rhs` is operand.
addrspace_cast,
+ /// Implement builtin `@cVaArg`.
+ /// `operand` is payload index to `BinNode`.
+ c_va_arg,
+ /// Implement builtin `@cVaStart`.
+ /// `operand` is payload index to `UnNode`.
+ c_va_copy,
+ /// Implement builtin `@cVaStart`.
+ /// `operand` is payload index to `UnNode`.
+ c_va_end,
+ /// Implement builtin `@cVaStart`.
+ /// `operand` is `src_node: i32`.
+ c_va_start,
pub const InstData = struct {
opcode: Extended,
diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig
index 8c87a005d5..f07a514e46 100644
--- a/src/arch/aarch64/CodeGen.zig
+++ b/src/arch/aarch64/CodeGen.zig
@@ -875,6 +875,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}),
.vector_store_elem => return self.fail("TODO implement vector_store_elem", .{}),
+ .c_va_arg => return self.fail("TODO implement c_va_arg", .{}),
+ .c_va_copy => return self.fail("TODO implement c_va_copy", .{}),
+ .c_va_end => return self.fail("TODO implement c_va_end", .{}),
+ .c_va_start => return self.fail("TODO implement c_va_start", .{}),
+
.wasm_memory_size => unreachable,
.wasm_memory_grow => unreachable,
// zig fmt: on
diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig
index 9d6d18d5c9..239f7c7de8 100644
--- a/src/arch/arm/CodeGen.zig
+++ b/src/arch/arm/CodeGen.zig
@@ -785,6 +785,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}),
.vector_store_elem => return self.fail("TODO implement vector_store_elem", .{}),
+ .c_va_arg => return self.fail("TODO implement c_va_arg", .{}),
+ .c_va_copy => return self.fail("TODO implement c_va_copy", .{}),
+ .c_va_end => return self.fail("TODO implement c_va_end", .{}),
+ .c_va_start => return self.fail("TODO implement c_va_start", .{}),
+
.wasm_memory_size => unreachable,
.wasm_memory_grow => unreachable,
// zig fmt: on
diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig
index 24e24076f7..2826cd057c 100644
--- a/src/arch/riscv64/CodeGen.zig
+++ b/src/arch/riscv64/CodeGen.zig
@@ -699,6 +699,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}),
.vector_store_elem => return self.fail("TODO implement vector_store_elem", .{}),
+ .c_va_arg => return self.fail("TODO implement c_va_arg", .{}),
+ .c_va_copy => return self.fail("TODO implement c_va_copy", .{}),
+ .c_va_end => return self.fail("TODO implement c_va_end", .{}),
+ .c_va_start => return self.fail("TODO implement c_va_start", .{}),
+
.wasm_memory_size => unreachable,
.wasm_memory_grow => unreachable,
// zig fmt: on
diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig
index 1672547f58..e0a8d75444 100644
--- a/src/arch/sparc64/CodeGen.zig
+++ b/src/arch/sparc64/CodeGen.zig
@@ -716,6 +716,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.error_set_has_value => @panic("TODO implement error_set_has_value"),
.vector_store_elem => @panic("TODO implement vector_store_elem"),
+ .c_va_arg => @panic("TODO implement c_va_arg"),
+ .c_va_copy => @panic("TODO implement c_va_copy"),
+ .c_va_end => @panic("TODO implement c_va_end"),
+ .c_va_start => @panic("TODO implement c_va_start"),
+
.wasm_memory_size => unreachable,
.wasm_memory_grow => unreachable,
// zig fmt: on
diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig
index 70a2aea3c9..0b85d30953 100644
--- a/src/arch/wasm/CodeGen.zig
+++ b/src/arch/wasm/CodeGen.zig
@@ -1972,6 +1972,10 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
.error_set_has_value,
.addrspace_cast,
.vector_store_elem,
+ .c_va_arg,
+ .c_va_copy,
+ .c_va_end,
+ .c_va_start,
=> |tag| return func.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),
.add_optimized,
diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig
index d0599150bd..a77b0be6cc 100644
--- a/src/arch/x86_64/CodeGen.zig
+++ b/src/arch/x86_64/CodeGen.zig
@@ -787,6 +787,11 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}),
.vector_store_elem => return self.fail("TODO implement vector_store_elem", .{}),
+ .c_va_arg => return self.fail("TODO implement c_va_arg", .{}),
+ .c_va_copy => return self.fail("TODO implement c_va_copy", .{}),
+ .c_va_end => return self.fail("TODO implement c_va_end", .{}),
+ .c_va_start => return self.fail("TODO implement c_va_start", .{}),
+
.wasm_memory_size => unreachable,
.wasm_memory_grow => unreachable,
// zig fmt: on
diff --git a/src/codegen/c.zig b/src/codegen/c.zig
index 97f67d3eec..a7a2b2cf2a 100644
--- a/src/codegen/c.zig
+++ b/src/codegen/c.zig
@@ -2909,6 +2909,11 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
.is_named_enum_value => return f.fail("TODO: C backend: implement is_named_enum_value", .{}),
.error_set_has_value => return f.fail("TODO: C backend: implement error_set_has_value", .{}),
.vector_store_elem => return f.fail("TODO: C backend: implement vector_store_elem", .{}),
+
+ .c_va_arg => return f.fail("TODO implement c_va_arg", .{}),
+ .c_va_copy => return f.fail("TODO implement c_va_copy", .{}),
+ .c_va_end => return f.fail("TODO implement c_va_end", .{}),
+ .c_va_start => return f.fail("TODO implement c_va_start", .{}),
// zig fmt: on
};
if (result_value == .local) {
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index cd9ef98e44..7af987f4d6 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -4699,6 +4699,11 @@ pub const FuncGen = struct {
.dbg_block_end => try self.airDbgBlockEnd(),
.dbg_var_ptr => try self.airDbgVarPtr(inst),
.dbg_var_val => try self.airDbgVarVal(inst),
+
+ .c_va_arg => try self.airCVaArg(inst),
+ .c_va_copy => try self.airCVaCopy(inst),
+ .c_va_end => try self.airCVaEnd(inst),
+ .c_va_start => try self.airCVaStart(inst),
// zig fmt: on
};
if (opt_value) |val| {
@@ -5136,6 +5141,94 @@ pub const FuncGen = struct {
return null;
}
+ fn airCVaArg(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
+ if (self.liveness.isUnused(inst)) return null;
+
+ const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+ const list = try self.resolveInst(ty_op.operand);
+ const arg_ty = self.air.getRefType(ty_op.ty);
+ const llvm_arg_ty = try self.dg.lowerType(arg_ty);
+
+ return self.builder.buildVAArg(list, llvm_arg_ty, "");
+ }
+
+ fn airCVaCopy(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
+ if (self.liveness.isUnused(inst)) return null;
+
+ const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+ const src_list = try self.resolveInst(ty_op.operand);
+ const va_list_ty = self.air.getRefType(ty_op.ty);
+ const llvm_va_list_ty = try self.dg.lowerType(va_list_ty);
+
+ const target = self.dg.module.getTarget();
+ const result_alignment = va_list_ty.abiAlignment(target);
+ const dest_list = self.buildAlloca(llvm_va_list_ty, result_alignment);
+
+ const llvm_fn_name = "llvm.va_copy";
+ const llvm_fn = self.dg.object.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: {
+ const param_types = [_]*llvm.Type{
+ self.dg.context.intType(8).pointerType(0),
+ self.dg.context.intType(8).pointerType(0),
+ };
+ const fn_type = llvm.functionType(self.context.voidType(), &param_types, param_types.len, .False);
+ break :blk self.dg.object.llvm_module.addFunction(llvm_fn_name, fn_type);
+ };
+
+ const args: [2]*llvm.Value = .{ dest_list, src_list };
+ _ = self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, &args, args.len, .Fast, .Auto, "");
+
+ if (isByRef(va_list_ty)) {
+ return dest_list;
+ } else {
+ const loaded = self.builder.buildLoad(llvm_va_list_ty, dest_list, "");
+ loaded.setAlignment(result_alignment);
+ return loaded;
+ }
+ }
+
+ fn airCVaEnd(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
+ const un_op = self.air.instructions.items(.data)[inst].un_op;
+ const list = try self.resolveInst(un_op);
+
+ const llvm_fn_name = "llvm.va_end";
+ const llvm_fn = self.dg.object.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: {
+ const param_types = [_]*llvm.Type{self.dg.context.intType(8).pointerType(0)};
+ const fn_type = llvm.functionType(self.context.voidType(), &param_types, param_types.len, .False);
+ break :blk self.dg.object.llvm_module.addFunction(llvm_fn_name, fn_type);
+ };
+ const args: [1]*llvm.Value = .{list};
+ _ = self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, &args, args.len, .Fast, .Auto, "");
+ return null;
+ }
+
+ fn airCVaStart(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
+ if (self.liveness.isUnused(inst)) return null;
+
+ const va_list_ty = self.air.typeOfIndex(inst);
+ const llvm_va_list_ty = try self.dg.lowerType(va_list_ty);
+
+ const target = self.dg.module.getTarget();
+ const result_alignment = va_list_ty.abiAlignment(target);
+ const list = self.buildAlloca(llvm_va_list_ty, result_alignment);
+
+ const llvm_fn_name = "llvm.va_start";
+ const llvm_fn = self.dg.object.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: {
+ const param_types = [_]*llvm.Type{self.dg.context.intType(8).pointerType(0)};
+ const fn_type = llvm.functionType(self.context.voidType(), &param_types, param_types.len, .False);
+ break :blk self.dg.object.llvm_module.addFunction(llvm_fn_name, fn_type);
+ };
+ const args: [1]*llvm.Value = .{list};
+ _ = self.builder.buildCall(llvm_fn.globalGetValueType(), llvm_fn, &args, args.len, .Fast, .Auto, "");
+
+ if (isByRef(va_list_ty)) {
+ return list;
+ } else {
+ const loaded = self.builder.buildLoad(llvm_va_list_ty, list, "");
+ loaded.setAlignment(result_alignment);
+ return loaded;
+ }
+ }
+
fn airCmp(self: *FuncGen, inst: Air.Inst.Index, op: math.CompareOperator, want_fast_math: bool) !?*llvm.Value {
if (self.liveness.isUnused(inst)) return null;
self.builder.setFastMath(want_fast_math);
diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig
index 90d0f51c7b..fc12b4a8c1 100644
--- a/src/codegen/llvm/bindings.zig
+++ b/src/codegen/llvm/bindings.zig
@@ -965,6 +965,9 @@ pub const Builder = opaque {
pub const buildAllocaInAddressSpace = ZigLLVMBuildAllocaInAddressSpace;
extern fn ZigLLVMBuildAllocaInAddressSpace(B: *Builder, Ty: *Type, AddressSpace: c_uint, Name: [*:0]const u8) *Value;
+
+ pub const buildVAArg = LLVMBuildVAArg;
+ extern fn LLVMBuildVAArg(*Builder, List: *Value, Ty: *Type, Name: [*:0]const u8) *Value;
};
pub const MDString = opaque {
diff --git a/src/print_air.zig b/src/print_air.zig
index 0344595a91..1184a9f33a 100644
--- a/src/print_air.zig
+++ b/src/print_air.zig
@@ -191,6 +191,7 @@ const Writer = struct {
.neg_optimized,
.cmp_lt_errors_len,
.set_err_return_trace,
+ .c_va_end,
=> try w.writeUnOp(s, inst),
.breakpoint,
@@ -205,6 +206,7 @@ const Writer = struct {
.ret_ptr,
.arg,
.err_return_trace,
+ .c_va_start,
=> try w.writeTy(s, inst),
.not,
@@ -246,6 +248,8 @@ const Writer = struct {
.bit_reverse,
.error_set_has_value,
.addrspace_cast,
+ .c_va_arg,
+ .c_va_copy,
=> try w.writeTyOp(s, inst),
.block,
diff --git a/src/print_zir.zig b/src/print_zir.zig
index 6dbaf51bc3..c518bfa431 100644
--- a/src/print_zir.zig
+++ b/src/print_zir.zig
@@ -465,6 +465,7 @@ const Writer = struct {
.frame,
.frame_address,
.breakpoint,
+ .c_va_start,
=> try self.writeExtNode(stream, extended),
.builtin_src => {
@@ -504,6 +505,8 @@ const Writer = struct {
.error_to_int,
.int_to_error,
.reify,
+ .c_va_copy,
+ .c_va_end,
=> {
const inst_data = self.code.extraData(Zir.Inst.UnNode, extended.operand).data;
const src = LazySrcLoc.nodeOffset(inst_data.node);
@@ -518,6 +521,7 @@ const Writer = struct {
.wasm_memory_grow,
.prefetch,
.addrspace_cast,
+ .c_va_arg,
=> {
const inst_data = self.code.extraData(Zir.Inst.BinNode, extended.operand).data;
const src = LazySrcLoc.nodeOffset(inst_data.node);