cbe: get enough things working to support basic programs

* Enable advanced start support.
 * Enable advanced test_runner support.
 * Zig Language Reference's Hello World now works.
This commit is contained in:
Jacob Young 2022-10-19 07:56:02 -04:00
parent 912b84bbad
commit 4765294ca4
11 changed files with 319 additions and 196 deletions

7
lib/include/zig.h vendored
View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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),
);
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"),
},
.i386 => {
argc_argv_ptr = asm volatile (
\\ xor %%ebp, %%ebp
: [argc] "={esp}" (-> [*]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"),
},
.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"),
}
// 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, .{});
}

View File

@ -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");
}

View File

@ -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();
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());
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)});
}
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",

View File

@ -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) {

View File

@ -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"));
}

View File

@ -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 {

View File

@ -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

View File

@ -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