Commit 4765294ca4
Changed files (11)
lib
test
behavior
lib/include/zig.h
@@ -14,6 +14,12 @@
#define zig_threadlocal zig_threadlocal_unavailable
#endif
+#if defined(_MSC_VER)
+#define ZIG_NAKED __declspec(naked)
+#else
+#define ZIG_NAKED __attribute__((naked))
+#endif
+
#if __GNUC__
#define ZIG_COLD __attribute__ ((cold))
#else
@@ -165,7 +171,6 @@
#include <stdint.h>
#include <stddef.h>
#include <limits.h>
-#include <math.h>
#define int128_t __int128
#define uint128_t unsigned __int128
lib/std/os/linux/x86_64.zig
@@ -108,11 +108,18 @@ pub extern fn clone(func: CloneFn, stack: usize, flags: usize, arg: usize, ptid:
pub const restore = restore_rt;
pub fn restore_rt() callconv(.Naked) void {
- return asm volatile ("syscall"
- :
- : [number] "{rax}" (@enumToInt(SYS.rt_sigreturn)),
- : "rcx", "r11", "memory"
- );
+ switch (@import("builtin").zig_backend) {
+ .stage2_c => return asm volatile (std.fmt.comptimePrint(
+ \\ movl ${d}, %%eax
+ \\ syscall
+ \\ retq
+ , .{@enumToInt(SYS.rt_sigreturn)}) ::: "rcx", "r11", "memory"),
+ else => return asm volatile ("syscall"
+ :
+ : [number] "{rax}" (@enumToInt(SYS.rt_sigreturn)),
+ : "rcx", "r11", "memory"
+ ),
+ }
}
pub const mode_t = usize;
lib/std/debug.zig
@@ -1222,7 +1222,13 @@ pub const DebugInfo = struct {
}
pub fn getModuleForAddress(self: *DebugInfo, address: usize) !*ModuleDebugInfo {
- if (comptime builtin.target.isDarwin()) {
+ if (builtin.zig_backend == .stage2_c) {
+ return @as(error{
+ InvalidDebugInfo,
+ MissingDebugInfo,
+ UnsupportedBackend,
+ }, error.UnsupportedBackend);
+ } else if (comptime builtin.target.isDarwin()) {
return self.lookupModuleDyld(address);
} else if (native_os == .windows) {
return self.lookupModuleWin32(address);
lib/std/start.zig
@@ -23,7 +23,6 @@ comptime {
// Until then, we have simplified logic here for self-hosted. TODO remove this once
// self-hosted is capable enough to handle all of the real start.zig logic.
if (builtin.zig_backend == .stage2_wasm or
- builtin.zig_backend == .stage2_c or
builtin.zig_backend == .stage2_x86_64 or
builtin.zig_backend == .stage2_x86 or
builtin.zig_backend == .stage2_aarch64 or
@@ -265,75 +264,90 @@ fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) callconv
}
fn _start() callconv(.Naked) noreturn {
- switch (native_arch) {
- .x86_64 => {
- argc_argv_ptr = asm volatile (
- \\ xor %%rbp, %%rbp
- : [argc] "={rsp}" (-> [*]usize),
- );
- },
- .i386 => {
- argc_argv_ptr = asm volatile (
- \\ xor %%ebp, %%ebp
- : [argc] "={esp}" (-> [*]usize),
- );
- },
- .aarch64, .aarch64_be, .arm, .armeb, .thumb => {
- argc_argv_ptr = asm volatile (
- \\ mov fp, #0
- \\ mov lr, #0
- : [argc] "={sp}" (-> [*]usize),
- );
- },
- .riscv64 => {
- argc_argv_ptr = asm volatile (
- \\ li s0, 0
- \\ li ra, 0
- : [argc] "={sp}" (-> [*]usize),
- );
- },
- .mips, .mipsel => {
- // The lr is already zeroed on entry, as specified by the ABI.
- argc_argv_ptr = asm volatile (
- \\ move $fp, $0
- : [argc] "={sp}" (-> [*]usize),
- );
- },
- .powerpc => {
- // Setup the initial stack frame and clear the back chain pointer.
- argc_argv_ptr = asm volatile (
- \\ mr 4, 1
- \\ li 0, 0
- \\ stwu 1,-16(1)
- \\ stw 0, 0(1)
- \\ mtlr 0
- : [argc] "={r4}" (-> [*]usize),
- :
- : "r0"
- );
- },
- .powerpc64le => {
- // Setup the initial stack frame and clear the back chain pointer.
- // TODO: Support powerpc64 (big endian) on ELFv2.
- argc_argv_ptr = asm volatile (
- \\ mr 4, 1
- \\ li 0, 0
- \\ stdu 0, -32(1)
- \\ mtlr 0
- : [argc] "={r4}" (-> [*]usize),
- :
- : "r0"
- );
+ switch (builtin.zig_backend) {
+ .stage2_c => switch (native_arch) {
+ .x86_64 => {
+ @export(argc_argv_ptr, .{ .name = "argc_argv_ptr" });
+ @export(posixCallMainAndExit, .{ .name = "_posixCallMainAndExit" });
+ asm volatile (
+ \\ xor %%rbp, %%rbp
+ \\ mov %%rsp, argc_argv_ptr
+ \\ call _posixCallMainAndExit
+ );
+ unreachable;
+ },
+ else => @compileError("unsupported arch"),
},
- .sparc64 => {
- // argc is stored after a register window (16 registers) plus stack bias
- argc_argv_ptr = asm (
- \\ mov %%g0, %%i6
- \\ add %%o6, 2175, %[argc]
- : [argc] "=r" (-> [*]usize),
- );
+ else => switch (native_arch) {
+ .x86_64 => {
+ argc_argv_ptr = asm volatile (
+ \\ xor %%rbp, %%rbp
+ : [argc] "={rsp}" (-> [*]usize),
+ );
+ },
+ .i386 => {
+ argc_argv_ptr = asm volatile (
+ \\ xor %%ebp, %%ebp
+ : [argc] "={esp}" (-> [*]usize),
+ );
+ },
+ .aarch64, .aarch64_be, .arm, .armeb, .thumb => {
+ argc_argv_ptr = asm volatile (
+ \\ mov fp, #0
+ \\ mov lr, #0
+ : [argc] "={sp}" (-> [*]usize),
+ );
+ },
+ .riscv64 => {
+ argc_argv_ptr = asm volatile (
+ \\ li s0, 0
+ \\ li ra, 0
+ : [argc] "={sp}" (-> [*]usize),
+ );
+ },
+ .mips, .mipsel => {
+ // The lr is already zeroed on entry, as specified by the ABI.
+ argc_argv_ptr = asm volatile (
+ \\ move $fp, $0
+ : [argc] "={sp}" (-> [*]usize),
+ );
+ },
+ .powerpc => {
+ // Setup the initial stack frame and clear the back chain pointer.
+ argc_argv_ptr = asm volatile (
+ \\ mr 4, 1
+ \\ li 0, 0
+ \\ stwu 1,-16(1)
+ \\ stw 0, 0(1)
+ \\ mtlr 0
+ : [argc] "={r4}" (-> [*]usize),
+ :
+ : "r0"
+ );
+ },
+ .powerpc64le => {
+ // Setup the initial stack frame and clear the back chain pointer.
+ // TODO: Support powerpc64 (big endian) on ELFv2.
+ argc_argv_ptr = asm volatile (
+ \\ mr 4, 1
+ \\ li 0, 0
+ \\ stdu 0, -32(1)
+ \\ mtlr 0
+ : [argc] "={r4}" (-> [*]usize),
+ :
+ : "r0"
+ );
+ },
+ .sparc64 => {
+ // argc is stored after a register window (16 registers) plus stack bias
+ argc_argv_ptr = asm (
+ \\ mov %%g0, %%i6
+ \\ add %%o6, 2175, %[argc]
+ : [argc] "=r" (-> [*]usize),
+ );
+ },
+ else => @compileError("unsupported arch"),
},
- else => @compileError("unsupported arch"),
}
// If LLVM inlines stack variables into _start, they will overwrite
// the command line argument data.
@@ -363,7 +377,7 @@ fn wWinMainCRTStartup() callconv(std.os.windows.WINAPI) noreturn {
std.os.windows.kernel32.ExitProcess(@bitCast(std.os.windows.UINT, result));
}
-fn posixCallMainAndExit() noreturn {
+fn posixCallMainAndExit() callconv(.C) noreturn {
@setAlignStack(16);
const argc = argc_argv_ptr[0];
@@ -462,7 +476,7 @@ fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [][*:0]u8) u8 {
return initEventLoopAndCallMain();
}
-fn main(c_argc: i32, c_argv: [*][*:0]u8, c_envp: [*:null]?[*:0]u8) callconv(.C) i32 {
+fn main(c_argc: c_int, c_argv: [*c][*c]u8, c_envp: [*c][*c]u8) callconv(.C) c_int {
var env_count: usize = 0;
while (c_envp[env_count] != null) : (env_count += 1) {}
const envp = @ptrCast([*][*:0]u8, c_envp)[0..env_count];
@@ -474,10 +488,10 @@ fn main(c_argc: i32, c_argv: [*][*:0]u8, c_envp: [*:null]?[*:0]u8) callconv(.C)
expandStackSize(phdrs);
}
- return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), c_argv, envp });
+ return @call(.{ .modifier = .always_inline }, callMainWithArgs, .{ @intCast(usize, c_argc), @ptrCast([*][*:0]u8, c_argv), envp });
}
-fn mainWithoutEnv(c_argc: i32, c_argv: [*][*:0]u8) callconv(.C) usize {
+fn mainWithoutEnv(c_argc: c_int, c_argv: [*c][*c]u8) callconv(.C) c_int {
std.os.argv = c_argv[0..@intCast(usize, c_argc)];
return @call(.{ .modifier = .always_inline }, callMain, .{});
}
lib/test_runner.zig
@@ -8,7 +8,8 @@ var log_err_count: usize = 0;
pub fn main() void {
if (builtin.zig_backend != .stage1 and
- (builtin.zig_backend != .stage2_llvm or builtin.cpu.arch == .wasm32))
+ (builtin.zig_backend != .stage2_llvm or builtin.cpu.arch == .wasm32) and
+ builtin.zig_backend != .stage2_c)
{
return main2() catch @panic("test failure");
}
src/codegen/c.zig
@@ -34,8 +34,8 @@ pub const CValue = union(enum) {
/// By-value
decl: Decl.Index,
decl_ref: Decl.Index,
- /// An undefined (void *) pointer (cannot be dereferenced)
- undefined_ptr: Type,
+ /// An undefined value (cannot be dereferenced)
+ undef: Type,
/// Render the slice as an identifier (using fmtIdent)
identifier: []const u8,
/// Render these bytes literally.
@@ -342,8 +342,9 @@ pub const Function = struct {
const float_bits = float_ty.floatBits(target);
const is_longdouble = float_bits == CType.longdouble.sizeInBits(target);
const writer = f.object.writer();
- if (!is_longdouble and float_bits == 80) {
- try writer.writeAll("__");
+ try writer.writeAll("__");
+ if (is_longdouble or float_bits != 80) {
+ try writer.writeAll("builtin_");
}
try writer.writeAll(fn_name);
if (is_longdouble) {
@@ -410,10 +411,17 @@ pub const DeclGen = struct {
const decl = dg.module.declPtr(decl_index);
assert(decl.has_tv);
+ // Render an undefined pointer if we have a pointer to a zero-bit or comptime type.
if (ty.isPtrAtRuntime() and !decl.ty.isFnOrHasRuntimeBits()) {
- return dg.writeCValue(writer, CValue{ .undefined_ptr = ty });
+ return dg.writeCValue(writer, CValue{ .undef = ty });
}
+ // Chase function values in order to be able to reference the original function.
+ inline for (.{ .function, .extern_fn }) |tag|
+ if (decl.val.castTag(tag)) |func|
+ if (func.data.owner_decl != decl_index)
+ return dg.renderDeclValue(writer, ty, val, func.data.owner_decl);
+
if (ty.isSlice()) {
try writer.writeByte('(');
try dg.renderTypecast(writer, ty);
@@ -588,9 +596,9 @@ pub const DeclGen = struct {
try writer.writeByte('(');
try dg.renderTypecast(writer, ty);
try writer.writeAll("){ .payload = ");
- try dg.renderValue(writer, Type.bool, val, location);
- try writer.writeAll(", .is_null = ");
try dg.renderValue(writer, payload_ty, val, location);
+ try writer.writeAll(", .is_null = ");
+ try dg.renderValue(writer, Type.bool, val, location);
return writer.writeAll(" }");
},
.Struct => {
@@ -635,7 +643,7 @@ pub const DeclGen = struct {
try writer.writeAll("){ .payload = ");
try dg.renderValue(writer, ty.errorUnionPayload(), val, location);
return writer.print(", .error = {x} }}", .{
- try dg.fmtIntLiteral(ty.errorUnionSet(), Value.undef),
+ try dg.fmtIntLiteral(ty.errorUnionSet(), val),
});
},
.Array => {
@@ -844,13 +852,13 @@ pub const DeclGen = struct {
}
},
.ErrorUnion => {
- const error_type = ty.errorUnionSet();
- const payload_type = ty.errorUnionPayload();
+ const error_ty = ty.errorUnionSet();
+ const payload_ty = ty.errorUnionPayload();
- if (!payload_type.hasRuntimeBits()) {
+ if (!payload_ty.hasRuntimeBits()) {
// We use the error type directly as the type.
const err_val = if (val.errorUnionIsPayload()) Value.initTag(.zero) else val;
- return dg.renderValue(writer, error_type, err_val, location);
+ return dg.renderValue(writer, error_ty, err_val, location);
}
try writer.writeByte('(');
@@ -859,11 +867,15 @@ pub const DeclGen = struct {
if (val.castTag(.eu_payload)) |pl| {
const payload_val = pl.data;
try writer.writeAll(" .payload = ");
- try dg.renderValue(writer, payload_type, payload_val, location);
- try writer.writeAll(", .error = 0 }");
+ try dg.renderValue(writer, payload_ty, payload_val, location);
+ try writer.print(", .error = {} }}", .{
+ try dg.fmtIntLiteral(error_ty, Value.zero),
+ });
} else {
- try writer.writeAll(" .error = ");
- try dg.renderValue(writer, error_type, val, location);
+ try writer.writeAll(" .payload = ");
+ try dg.renderValue(writer, payload_ty, Value.undef, location);
+ try writer.writeAll(", .error = ");
+ try dg.renderValue(writer, error_ty, val, location);
try writer.writeAll(" }");
}
},
@@ -987,13 +999,16 @@ pub const DeclGen = struct {
if (!is_global) {
try w.writeAll("static ");
}
+ const fn_info = dg.decl.ty.fnInfo();
+ if (fn_info.cc == .Naked) {
+ try w.writeAll("ZIG_NAKED ");
+ }
if (dg.decl.val.castTag(.function)) |func_payload| {
const func: *Module.Fn = func_payload.data;
if (func.is_cold) {
try w.writeAll("ZIG_COLD ");
}
}
- const fn_info = dg.decl.ty.fnInfo();
if (fn_info.return_type.hasRuntimeBits()) {
try dg.renderType(w, fn_info.return_type);
} else if (fn_info.return_type.isError()) {
@@ -1007,21 +1022,21 @@ pub const DeclGen = struct {
try dg.renderDeclName(w, dg.decl_index);
try w.writeByte('(');
- var params_written: usize = 0;
- for (fn_info.param_types) |param_type, index| {
+ var index: usize = 0;
+ for (fn_info.param_types) |param_type| {
if (!param_type.hasRuntimeBitsIgnoreComptime()) continue;
- if (params_written > 0) {
+ if (index > 0) {
try w.writeAll(", ");
}
const name = CValue{ .arg = index };
try dg.renderTypeAndName(w, param_type, name, .Mut, 0);
- params_written += 1;
+ index += 1;
}
if (fn_info.is_var_args) {
- if (params_written != 0) try w.writeAll(", ");
+ if (index > 0) try w.writeAll(", ");
try w.writeAll("...");
- } else if (params_written == 0) {
+ } else if (index == 0) {
try w.writeAll("void");
}
try w.writeByte(')');
@@ -1307,7 +1322,7 @@ pub const DeclGen = struct {
fn renderErrorUnionTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
const payload_ty = t.errorUnionPayload();
- const error_ty = t.errorUnionSet();
+ assert(t.errorUnionSet().tag() == .anyerror);
var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
defer buffer.deinit();
@@ -1328,15 +1343,7 @@ pub const DeclGen = struct {
}
const name_begin = buffer.items.len;
- if (error_ty.castTag(.error_set_inferred)) |inf_err_set_payload| {
- const func = inf_err_set_payload.data.func;
- try bw.writeAll("zig_E_");
- try dg.renderDeclName(bw, func.owner_decl);
- } else {
- try bw.print("zig_E_{}_{}", .{
- typeToCIdentifier(error_ty, dg.module), typeToCIdentifier(payload_ty, dg.module),
- });
- }
+ try bw.print("zig_E_{}", .{typeToCIdentifier(payload_ty, dg.module)});
const name_end = buffer.items.len;
try bw.writeAll(";\n");
@@ -1505,11 +1512,11 @@ pub const DeclGen = struct {
},
.Pointer => {
if (t.isSlice()) {
- var slice_ty_pl = Type.Payload.ElemType{
+ var slice_pl = Type.Payload.ElemType{
.base = .{ .tag = if (t.ptrIsMutable()) .mut_slice else .const_slice },
.data = t.childType(),
};
- const slice_ty = Type.initPayload(&slice_ty_pl.base);
+ const slice_ty = Type.initPayload(&slice_pl.base);
const name = dg.getTypedefName(slice_ty) orelse
try dg.renderSliceTypedef(slice_ty);
@@ -1525,7 +1532,9 @@ pub const DeclGen = struct {
}
const child_ty = t.childType();
- if (t.isCPtr() and child_ty.eql(Type.u8, dg.module) and dg.decl.val.tag() == .extern_fn) {
+ if (t.isCPtr() and child_ty.eql(Type.u8, dg.module) and
+ (dg.decl.val.tag() == .extern_fn or std.mem.eql(u8, std.mem.span(dg.decl.name), "main")))
+ {
// This is a hack, since the c compiler expects a lot of external
// library functions to have char pointers in their signatures, but
// u8 and i8 produce unsigned char and signed char respectively,
@@ -1543,11 +1552,11 @@ pub const DeclGen = struct {
return w.writeAll(" *");
},
.Array => {
- var array_ty_pl = Type.Payload.Array{ .base = .{ .tag = .array }, .data = .{
+ var array_pl = Type.Payload.Array{ .base = .{ .tag = .array }, .data = .{
.len = t.arrayLenIncludingSentinel(),
.elem_type = t.childType(),
} };
- const array_ty = Type.initPayload(&array_ty_pl.base);
+ const array_ty = Type.initPayload(&array_pl.base);
const name = dg.getTypedefName(array_ty) orelse
try dg.renderArrayTypedef(array_ty);
@@ -1582,8 +1591,13 @@ pub const DeclGen = struct {
return dg.renderType(w, Type.anyerror);
}
- const name = dg.getTypedefName(t) orelse
- try dg.renderErrorUnionTypedef(t);
+ var error_union_pl = Type.Payload.ErrorUnion{
+ .data = .{ .error_set = Type.anyerror, .payload = payload_ty },
+ };
+ const error_union_ty = Type.initPayload(&error_union_pl.base);
+
+ const name = dg.getTypedefName(error_union_ty) orelse
+ try dg.renderErrorUnionTypedef(error_union_ty);
return w.writeAll(name);
},
@@ -1603,8 +1617,8 @@ pub const DeclGen = struct {
},
.Enum => {
// For enums, we simply use the integer tag type.
- var int_tag_ty_buffer: Type.Payload.Bits = undefined;
- const int_tag_ty = t.intTagType(&int_tag_ty_buffer);
+ var int_tag_buf: Type.Payload.Bits = undefined;
+ const int_tag_ty = t.intTagType(&int_tag_buf);
try dg.renderType(w, int_tag_ty);
},
@@ -1801,11 +1815,7 @@ pub const DeclGen = struct {
try w.writeByte('&');
return dg.renderDeclName(w, decl);
},
- .undefined_ptr => |ty| {
- try w.writeAll("((");
- try dg.renderTypecast(w, ty);
- return w.print("){x})", .{try dg.fmtIntLiteral(Type.usize, Value.undef)});
- },
+ .undef => |ty| return dg.renderValue(w, ty, Value.undef, .Other),
.identifier => |ident| return w.print("{ }", .{fmtIdent(ident)}),
.bytes => |bytes| return w.writeAll(bytes),
}
@@ -1824,7 +1834,7 @@ pub const DeclGen = struct {
return w.writeByte(')');
},
.decl_ref => |decl| return dg.renderDeclName(w, decl),
- .undefined_ptr => unreachable,
+ .undef => unreachable,
.identifier => |ident| return w.print("(*{ })", .{fmtIdent(ident)}),
.bytes => |bytes| {
try w.writeAll("(*");
@@ -2151,10 +2161,10 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
.mul_add => try airMulAdd(f, inst),
- .add_with_overflow => try airOverflow(f, inst, "addo_"),
- .sub_with_overflow => try airOverflow(f, inst, "subo_"),
- .mul_with_overflow => try airOverflow(f, inst, "mulo_"),
- .shl_with_overflow => try airOverflow(f, inst, "shlo_"),
+ .add_with_overflow => try airOverflow(f, inst, "addo_", .range),
+ .sub_with_overflow => try airOverflow(f, inst, "subo_", .range),
+ .mul_with_overflow => try airOverflow(f, inst, "mulo_", .range),
+ .shl_with_overflow => try airOverflow(f, inst, "shlo_", .bits),
.min => try airMinMax(f, inst, "<"),
.max => try airMinMax(f, inst, ">"),
@@ -2284,8 +2294,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
.slice_ptr => try airSliceField(f, inst, " = ", ".ptr;\n"),
.slice_len => try airSliceField(f, inst, " = ", ".len;\n"),
- .ptr_slice_len_ptr => try airSliceField(f, inst, " = &", ".len;\n"),
- .ptr_slice_ptr_ptr => try airSliceField(f, inst, " = &", ".ptr;\n"),
+ .ptr_slice_len_ptr => try airSliceField(f, inst, " = &", "->len;\n"),
+ .ptr_slice_ptr_ptr => try airSliceField(f, inst, " = &", "->ptr;\n"),
.ptr_elem_val => try airPtrElemVal(f, inst),
.ptr_elem_ptr => try airPtrElemPtr(f, inst),
@@ -2459,7 +2469,7 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {
const elem_type = inst_ty.elemType();
const mutability: Mutability = if (inst_ty.isConstPtr()) .Const else .Mut;
if (!elem_type.isFnOrHasRuntimeBitsIgnoreComptime()) {
- return CValue{ .undefined_ptr = inst_ty };
+ return CValue{ .undef = inst_ty };
}
const target = f.object.dg.module.getTarget();
@@ -2474,9 +2484,13 @@ fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue {
const writer = f.object.writer();
const inst_ty = f.air.typeOfIndex(inst);
+ const elem_ty = inst_ty.elemType();
+ if (!elem_ty.hasRuntimeBitsIgnoreComptime()) {
+ return CValue{ .undef = inst_ty };
+ }
+
// First line: the variable used as data storage.
- const elem_type = inst_ty.elemType();
- const local = try f.allocLocal(elem_type, .Mut);
+ const local = try f.allocLocal(elem_ty, .Mut);
try writer.writeAll(";\n");
return CValue{ .local_ref = local.local };
@@ -2507,8 +2521,7 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
if (is_array) {
// Insert a memcpy to initialize this array. The source operand is always a pointer
// and thus we only need to know size/type information from the local type/dest.
- try writer.writeByte(';');
- try f.object.indent_writer.insertNewline();
+ try writer.writeAll(";\n");
try writer.writeAll("memcpy(");
try f.writeCValue(writer, local);
try writer.writeAll(", ");
@@ -2535,7 +2548,8 @@ fn airRet(f: *Function, inst: Air.Inst.Index) !CValue {
try writer.writeAll(";\n");
} else if (ret_ty.isError()) {
try writer.writeAll("return 0;");
- } else {
+ } else if (f.object.dg.decl.ty.fnCallingConvention() != .Naked) {
+ // Not even allowed to return void in a naked function.
try writer.writeAll("return;\n");
}
return CValue.none;
@@ -2847,7 +2861,7 @@ fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !CValue {
return ret;
}
-fn airOverflow(f: *Function, inst: Air.Inst.Index, op_abbrev: [*:0]const u8) !CValue {
+fn airOverflow(f: *Function, inst: Air.Inst.Index, op_abbrev: [*:0]const u8, kind: enum { range, bits }) !CValue {
if (f.liveness.isUnused(inst))
return CValue.none;
@@ -2879,23 +2893,29 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, op_abbrev: [*:0]const u8) !CV
try w.writeAll(", &");
try f.writeCValue(w, ret);
try w.writeAll(".field_0, ");
- {
- var arena = std.heap.ArenaAllocator.init(f.object.dg.module.gpa);
- defer arena.deinit();
-
- const expected_contents = union { u: Value.Payload.U64, i: Value.Payload.I64 };
- var stack align(@alignOf(expected_contents)) =
- std.heap.stackFallback(@sizeOf(expected_contents), arena.allocator());
-
- if (int_info.signedness == .signed) {
- const min_val = try scalar_ty.minInt(stack.get(), target);
- try w.print("{}, ", .{try f.fmtIntLiteral(scalar_ty, min_val)});
- }
+ switch (kind) {
+ .range => {
+ var arena = std.heap.ArenaAllocator.init(f.object.dg.module.gpa);
+ defer arena.deinit();
+
+ const expected_contents = union { u: Value.Payload.U64, i: Value.Payload.I64 };
+ var stack align(@alignOf(expected_contents)) =
+ std.heap.stackFallback(@sizeOf(expected_contents), arena.allocator());
+
+ if (int_info.signedness == .signed) {
+ const min_val = try scalar_ty.minInt(stack.get(), target);
+ try w.print("{}, ", .{try f.fmtIntLiteral(scalar_ty, min_val)});
+ }
- const max_val = try scalar_ty.maxInt(stack.get(), target);
- try w.print("{});", .{try f.fmtIntLiteral(scalar_ty, max_val)});
+ const max_val = try scalar_ty.maxInt(stack.get(), target);
+ try w.print("{});\n", .{try f.fmtIntLiteral(scalar_ty, max_val)});
+ },
+ .bits => {
+ var bits_pl = Value.Payload.U64{ .base = .{ .tag = .int_u64 }, .data = int_info.bits };
+ const bits_val = Value.initPayload(&bits_pl.base);
+ try w.print("{x});\n", .{try f.fmtIntLiteral(Type.u8, bits_val)});
+ },
}
- try f.object.indent_writer.insertNewline();
return ret;
}
@@ -3075,6 +3095,9 @@ fn airCall(
inst: Air.Inst.Index,
modifier: std.builtin.CallOptions.Modifier,
) !CValue {
+ // Not even allowed to call panic in a naked function.
+ if (f.object.dg.decl.ty.fnCallingConvention() == .Naked) return .none;
+
switch (modifier) {
.auto => {},
.always_tail => return f.fail("TODO: C backend: call with always_tail attribute", .{}),
@@ -3094,10 +3117,10 @@ fn airCall(
const writer = f.object.writer();
const result_local: CValue = r: {
- if (f.liveness.isUnused(inst)) {
- if (loweredFnRetTyHasBits(fn_ty)) {
- try writer.print("(void)", .{});
- }
+ if (!loweredFnRetTyHasBits(fn_ty)) {
+ break :r .none;
+ } else if (f.liveness.isUnused(inst)) {
+ try writer.print("(void)", .{});
break :r .none;
} else {
const local = try f.allocLocal(fn_ty.fnReturnType(), .Const);
@@ -3107,6 +3130,7 @@ fn airCall(
};
var is_extern = false;
+ var name: [*:0]const u8 = "";
callee: {
known: {
const fn_decl = fn_decl: {
@@ -3121,6 +3145,7 @@ fn airCall(
else => break :known,
};
};
+ name = f.object.dg.module.declPtr(fn_decl).name;
try f.object.dg.renderDeclName(writer, fn_decl);
break :callee;
}
@@ -3137,7 +3162,9 @@ fn airCall(
if (args_written != 0) {
try writer.writeAll(", ");
}
- if (is_extern and ty.isCPtr() and ty.childType().tag() == .u8) {
+ if ((is_extern or std.mem.eql(u8, std.mem.span(name), "main")) and
+ ty.isCPtr() and ty.childType().tag() == .u8)
+ {
// Corresponds with hack in renderType .Pointer case.
try writer.writeAll("(char");
if (ty.isConstPtr()) try writer.writeAll(" const");
@@ -3286,15 +3313,27 @@ fn lowerTry(
}
}
- const local = try f.allocLocal(result_ty, .Const);
- if (operand_is_ptr or isByRef(payload_ty)) {
- try writer.writeAll(" = &");
+ const is_array = payload_ty.zigTypeTag() == .Array;
+ const local = try f.allocLocal(result_ty, if (is_array) .Mut else .Const);
+ if (is_array) {
+ try writer.writeAll(";\n");
+ try writer.writeAll("memcpy(");
+ try f.writeCValue(writer, local);
+ try writer.writeAll(", ");
try f.writeCValue(writer, err_union);
- try writer.writeAll("->payload;\n");
+ try writer.writeAll(".payload, sizeof(");
+ try f.writeCValue(writer, local);
+ try writer.writeAll("));\n");
} else {
- try writer.writeAll(" = ");
- try f.writeCValue(writer, err_union);
- try writer.writeAll(".payload;\n");
+ if (operand_is_ptr or isByRef(payload_ty)) {
+ try writer.writeAll(" = &");
+ try f.writeCValue(writer, err_union);
+ try writer.writeAll("->payload;\n");
+ } else {
+ try writer.writeAll(" = ");
+ try f.writeCValue(writer, err_union);
+ try writer.writeAll(".payload;\n");
+ }
}
return local;
}
@@ -3361,15 +3400,21 @@ fn airBreakpoint(f: *Function) !CValue {
fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue {
if (f.liveness.isUnused(inst)) return CValue.none;
+ const writer = f.object.writer();
const local = try f.allocLocal(Type.usize, .Const);
- try f.object.writer().writeAll(" = zig_return_address();\n");
+ try writer.writeAll(" = (");
+ try f.renderTypecast(writer, Type.usize);
+ try writer.writeAll(")zig_return_address();\n");
return local;
}
fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue {
if (f.liveness.isUnused(inst)) return CValue.none;
+ const writer = f.object.writer();
const local = try f.allocLocal(Type.usize, .Const);
- try f.object.writer().writeAll(" = zig_frame_address();\n");
+ try writer.writeAll(" = (");
+ try f.renderTypecast(writer, Type.usize);
+ try writer.writeAll(")zig_frame_address();\n");
return local;
}
@@ -3385,6 +3430,9 @@ fn airFence(f: *Function, inst: Air.Inst.Index) !CValue {
}
fn airUnreach(f: *Function) !CValue {
+ // Not even allowed to call unreachable in a naked function.
+ if (f.object.dg.decl.ty.fnCallingConvention() == .Naked) return .none;
+
try f.object.writer().writeAll("zig_unreachable();\n");
return CValue.none;
}
@@ -3479,15 +3527,12 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
const local = try f.allocLocal(inst_ty, .Mut);
if (f.wantSafety()) {
try writer.writeAll(" = ");
- try f.object.dg.renderValue(writer, inst_ty, Value.undef, .Other);
+ try f.writeCValue(writer, .{ .undef = inst_ty });
}
try writer.writeAll(";\n");
break :local local;
} else .none;
- try writer.writeAll("{\n");
- f.object.indent_writer.pushIndent();
-
const output_locals_begin = f.next_local_index;
f.next_local_index += outputs.len;
const constraints_extra_begin = extra_i;
@@ -3510,7 +3555,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
try writer.writeAll("\")");
if (f.wantSafety()) {
try writer.writeAll(" = ");
- try f.object.dg.renderValue(writer, output_ty, Value.undef, .Other);
+ try f.writeCValue(writer, .{ .undef = output_ty });
}
try writer.writeAll(";\n");
} else if (constraint.len < 2 or constraint[0] != '=') {
@@ -3605,7 +3650,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
} else {
const input_val = try f.resolveInst(input);
try writer.print("{s}(", .{fmtStringLiteral(constraint)});
- try f.writeCValue(writer, if (input_val == .constant) .{
+ try f.writeCValue(writer, if (input_val == .constant) CValue{
.local = input_locals_begin + index,
} else input_val);
}
@@ -3647,9 +3692,6 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
}
}
- f.object.indent_writer.popIndent();
- try writer.writeAll("}\n");
-
return local;
}
@@ -3728,7 +3770,7 @@ fn airOptionalPayloadPtr(f: *Function, inst: Air.Inst.Index) !CValue {
const inst_ty = f.air.typeOfIndex(inst);
if (!inst_ty.childType().hasRuntimeBitsIgnoreComptime()) {
- return CValue{ .undefined_ptr = inst_ty };
+ return CValue{ .undef = inst_ty };
}
if (opt_ty.optionalReprIsPayload()) {
@@ -3996,7 +4038,9 @@ fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
}
const local = try f.allocLocal(err_un_ty, .Const);
- try writer.writeAll(" = { .error = ");
+ try writer.writeAll(" = { .payload = ");
+ try f.writeCValue(writer, .{ .undef = payload_ty });
+ try writer.writeAll(", .error = ");
try f.writeCValue(writer, operand);
try writer.writeAll(" };\n");
return local;
@@ -4059,10 +4103,25 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
const operand = try f.resolveInst(ty_op.operand);
const inst_ty = f.air.typeOfIndex(inst);
- const local = try f.allocLocal(inst_ty, .Const);
- try writer.writeAll(" = { .error = 0, .payload = ");
- try f.writeCValue(writer, operand);
- try writer.writeAll(" };\n");
+ const payload_ty = inst_ty.errorUnionPayload();
+ const is_array = payload_ty.zigTypeTag() == .Array;
+ const local = try f.allocLocal(inst_ty, if (is_array) .Mut else .Const);
+ try writer.writeAll(" = { .payload = ");
+ try f.writeCValue(writer, if (is_array) CValue{ .undef = payload_ty } else operand);
+ try writer.print(", .error = {} }};\n", .{
+ try f.fmtIntLiteral(inst_ty.errorUnionSet(), Value.zero),
+ });
+
+ if (is_array) {
+ try writer.writeAll("memcpy(");
+ try f.writeCValue(writer, local);
+ try writer.writeAll(".payload, ");
+ try f.writeCValue(writer, operand);
+ try writer.writeAll(", sizeof(");
+ try f.writeCValue(writer, local);
+ try writer.writeAll(".payload));\n");
+ }
+
return local;
}
@@ -4114,11 +4173,11 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {
const array_len = f.air.typeOf(ty_op.operand).elemType().arrayLen();
try writer.writeAll(" = { .ptr = ");
- if (operand == .undefined_ptr) {
+ if (operand == .undef) {
// Unfortunately, C does not support any equivalent to
// &(*(void *)p)[0], although LLVM does via GetElementPtr
var buf: Type.SlicePtrFieldTypeBuffer = undefined;
- try f.writeCValue(writer, CValue{ .undefined_ptr = inst_ty.slicePtrFieldType(&buf) });
+ try f.writeCValue(writer, CValue{ .undef = inst_ty.slicePtrFieldType(&buf) });
} else {
try writer.writeAll("&(");
try f.writeCValueDeref(writer, operand);
@@ -4528,9 +4587,12 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
const len = @intCast(usize, inst_ty.arrayLen());
const elements = @ptrCast([]const Air.Inst.Ref, f.air.extra[ty_pl.payload..][0..len]);
+ const mutability: Mutability = for (elements) |element| {
+ if (f.air.typeOf(element).zigTypeTag() == .Array) break .Mut;
+ } else .Const;
const writer = f.object.writer();
- const local = try f.allocLocal(inst_ty, .Const);
+ const local = try f.allocLocal(inst_ty, mutability);
try writer.writeAll(" = {");
switch (inst_ty.zigTypeTag()) {
.Array => {
@@ -4547,6 +4609,7 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
empty = false;
}
if (empty) try writer.print("{}", .{try f.fmtIntLiteral(Type.u8, Value.zero)});
+ try writer.writeAll("};\n");
},
.Struct => {
var empty = true;
@@ -4557,15 +4620,44 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
if (!inst_ty.isTupleOrAnonStruct()) {
try writer.print(".{ } = ", .{fmtIdent(inst_ty.structFieldName(index))});
}
- try f.writeCValue(writer, try f.resolveInst(element));
+
+ const element_ty = f.air.typeOf(element);
+ try f.writeCValue(writer, switch (element_ty.zigTypeTag()) {
+ .Array => CValue{ .undef = element_ty },
+ else => try f.resolveInst(element),
+ });
empty = false;
}
if (empty) try writer.print("{}", .{try f.fmtIntLiteral(Type.u8, Value.zero)});
+ try writer.writeAll("};\n");
+
+ for (elements) |element, index| {
+ if (inst_ty.structFieldValueComptime(index)) |_| continue;
+
+ const element_ty = f.air.typeOf(element);
+ if (element_ty.zigTypeTag() != .Array) continue;
+
+ var field_name_storage: ?[]u8 = null;
+ defer if (field_name_storage) |storage| f.object.dg.gpa.free(storage);
+ const field_name = if (inst_ty.isTupleOrAnonStruct()) field_name: {
+ const name = try std.fmt.allocPrint(f.object.dg.gpa, "field_{d}", .{index});
+ field_name_storage = name;
+ break :field_name name;
+ } else inst_ty.structFieldName(index);
+
+ try writer.writeAll(";\n");
+ try writer.writeAll("memcpy(");
+ try f.writeCValue(writer, local);
+ try writer.print(".{ }, ", .{fmtIdent(field_name)});
+ try f.writeCValue(writer, try f.resolveInst(element));
+ try writer.writeAll(", sizeof(");
+ try f.writeCValue(writer, local);
+ try writer.print(".{ }));\n", .{fmtIdent(field_name)});
+ }
},
.Vector => return f.fail("TODO: C backend: implement airAggregateInit for vectors", .{}),
else => unreachable,
}
- try writer.writeAll("};\n");
return local;
}
@@ -4732,8 +4824,8 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue {
fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 {
return switch (order) {
- .Unordered => "memory_order_relaxed",
- .Monotonic => "memory_order_consume",
+ // Note: unordered is actually even less atomic than relaxed
+ .Unordered, .Monotonic => "memory_order_relaxed",
.Acquire => "memory_order_acquire",
.Release => "memory_order_release",
.AcqRel => "memory_order_acq_rel",
src/main.zig
@@ -3018,7 +3018,10 @@ fn buildOutputType(
const c_code_path = try fs.path.join(arena, &[_][]const u8{
c_code_directory.path orelse ".", c_code_loc.basename,
});
- try test_exec_args.appendSlice(&.{ self_exe_path, "run", "-lc", c_code_path });
+ try test_exec_args.append(self_exe_path);
+ try test_exec_args.append("run");
+ if (link_libc) try test_exec_args.append("-lc");
+ try test_exec_args.append(c_code_path);
}
const run_or_test = switch (arg_mode) {
test/behavior/bugs/3742.zig
@@ -38,6 +38,5 @@ test "fixed" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
- if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
ArgSerializer.serializeCommand(GET.init("banana"));
}
test/behavior/fn.zig
@@ -272,7 +272,6 @@ test "void parameters" {
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
- if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
try voidFun(1, void{}, 2, {});
}
fn voidFun(a: i32, b: void, c: i32, d: void) !void {
test/behavior/struct.zig
@@ -1163,7 +1163,6 @@ test "for loop over pointers to struct, getting field from struct pointer" {
test "anon init through error unions and optionals" {
if (builtin.zig_backend == .stage1) return error.SkipZigTest;
- if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
@@ -1234,7 +1233,6 @@ test "anon init through error union" {
test "typed init through error unions and optionals" {
if (builtin.zig_backend == .stage1) return error.SkipZigTest;
- if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
test/behavior/translate_c_macros.zig
@@ -100,7 +100,6 @@ test "nested comma operator" {
test "cast functions" {
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
- if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO