Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 67 additions & 19 deletions src/codegen/c.zig
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ pub const CValue = union(enum) {
};

const BlockData = struct {
block_id: usize,
block_id: u32,
result: CValue,
};

Expand Down Expand Up @@ -359,8 +359,8 @@ pub const Function = struct {
liveness: Liveness,
value_map: CValueMap,
blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .empty,
next_arg_index: usize = 0,
next_block_index: usize = 0,
next_arg_index: u32 = 0,
next_block_index: u32 = 0,
object: Object,
lazy_fns: LazyFnMap,
func_index: InternPool.Index,
Expand Down Expand Up @@ -663,6 +663,7 @@ pub const DeclGen = struct {
mod: *Module,
pass: Pass,
is_naked_fn: bool,
expected_block: ?u32,
/// This is a borrowed reference from `link.C`.
fwd_decl: std.ArrayList(u8),
error_msg: ?*Zcu.ErrorMsg,
Expand Down Expand Up @@ -1399,12 +1400,24 @@ pub const DeclGen = struct {
.repeated_elem => |elem| elem,
};

const field_int_info: std.builtin.Type.Int = if (field_ty.isAbiInt(zcu))
field_ty.intInfo(zcu)
else
.{ .signedness = .unsigned, .bits = undefined };
switch (field_int_info.signedness) {
.signed => {
try writer.writeByte('(');
try dg.renderValue(writer, Value.fromInterned(field_val), .Other);
try writer.writeAll(" & ");
const field_uint_ty = try pt.intType(.unsigned, field_int_info.bits);
try dg.renderValue(writer, try field_uint_ty.maxIntScalar(pt, field_uint_ty), .Other);
try writer.writeByte(')');
},
.unsigned => try dg.renderValue(writer, Value.fromInterned(field_val), .Other),
}
if (bit_offset != 0) {
try dg.renderValue(writer, Value.fromInterned(field_val), .Other);
try writer.writeAll(" << ");
try dg.renderValue(writer, try pt.intValue(bit_offset_ty, bit_offset), .FunctionArgument);
} else {
try dg.renderValue(writer, Value.fromInterned(field_val), .Other);
}

bit_offset += field_ty.bitSize(zcu);
Expand Down Expand Up @@ -2899,6 +2912,8 @@ pub fn genFunc(f: *Function) !void {
const main_body = f.air.getMainBody();
try genBodyResolveState(f, undefined, &.{}, main_body, false);
try o.indent_writer.insertNewline();
if (o.dg.expected_block) |_|
return f.fail("runtime code not allowed in naked function", .{});

// Take advantage of the free_locals map to bucket locals per type. All
// locals corresponding to AIR instructions should be in there due to
Expand Down Expand Up @@ -3189,6 +3204,8 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
const air_datas = f.air.instructions.items(.data);

for (body) |inst| {
if (f.object.dg.expected_block) |_|
return f.fail("runtime code not allowed in naked function", .{});
if (f.liveness.isUnused(inst) and !f.air.mustLower(inst, ip))
continue;

Expand Down Expand Up @@ -4517,6 +4534,7 @@ fn airCall(
) !CValue {
const pt = f.object.dg.pt;
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
// Not even allowed to call panic in a naked function.
if (f.object.dg.is_naked_fn) return .none;

Expand Down Expand Up @@ -4562,11 +4580,12 @@ fn airCall(
}

const callee_ty = f.typeOf(pl_op.operand);
const fn_info = zcu.typeToFunc(switch (callee_ty.zigTypeTag(zcu)) {
.@"fn" => callee_ty,
.pointer => callee_ty.childType(zcu),
const callee_is_ptr = switch (callee_ty.zigTypeTag(zcu)) {
.@"fn" => false,
.pointer => true,
else => unreachable,
}).?;
};
const fn_info = zcu.typeToFunc(if (callee_is_ptr) callee_ty.childType(zcu) else callee_ty).?;
const ret_ty = Type.fromInterned(fn_info.return_type);
const ret_ctype: CType = if (ret_ty.isNoReturn(zcu))
CType.void
Expand Down Expand Up @@ -4598,20 +4617,29 @@ fn airCall(
callee: {
known: {
const callee_val = (try f.air.value(pl_op.operand, pt)) orelse break :known;
const fn_nav = switch (zcu.intern_pool.indexToKey(callee_val.toIntern())) {
.@"extern" => |@"extern"| @"extern".owner_nav,
.func => |func| func.owner_nav,
const fn_nav, const need_cast = switch (ip.indexToKey(callee_val.toIntern())) {
.@"extern" => |@"extern"| .{ @"extern".owner_nav, false },
.func => |func| .{ func.owner_nav, Type.fromInterned(func.ty).fnCallingConvention(zcu) != .naked and
Type.fromInterned(func.uncoerced_ty).fnCallingConvention(zcu) == .naked },
.ptr => |ptr| if (ptr.byte_offset == 0) switch (ptr.base_addr) {
.nav => |nav| nav,
.nav => |nav| .{ nav, Type.fromInterned(ptr.ty).childType(zcu).fnCallingConvention(zcu) != .naked and
zcu.navValue(nav).typeOf(zcu).fnCallingConvention(zcu) == .naked },
else => break :known,
} else break :known,
else => break :known,
};
if (need_cast) {
try writer.writeAll("((");
try f.renderType(writer, if (callee_is_ptr) callee_ty else try pt.singleConstPtrType(callee_ty));
try writer.writeByte(')');
if (!callee_is_ptr) try writer.writeByte('&');
}
switch (modifier) {
.auto, .always_tail => try f.object.dg.renderNavName(writer, fn_nav),
inline .never_tail, .never_inline => |m| try writer.writeAll(try f.getLazyFnName(@unionInit(LazyFnKey, @tagName(m), fn_nav))),
else => unreachable,
}
if (need_cast) try writer.writeByte(')');
break :callee;
}
switch (modifier) {
Expand Down Expand Up @@ -4712,7 +4740,7 @@ fn lowerBlock(f: *Function, inst: Air.Inst.Index, body: []const Air.Inst.Index)
const zcu = pt.zcu;
const liveness_block = f.liveness.getBlock(inst);

const block_id: usize = f.next_block_index;
const block_id = f.next_block_index;
f.next_block_index += 1;
const writer = f.object.writer();

Expand All @@ -4739,7 +4767,13 @@ fn lowerBlock(f: *Function, inst: Air.Inst.Index, body: []const Air.Inst.Index)
try f.object.indent_writer.insertNewline();

// noreturn blocks have no `br` instructions reaching them, so we don't want a label
if (!f.typeOfIndex(inst).isNoReturn(zcu)) {
if (f.object.dg.is_naked_fn) {
if (f.object.dg.expected_block) |expected_block| {
if (block_id != expected_block)
return f.fail("runtime code not allowed in naked function", .{});
f.object.dg.expected_block = null;
}
} else if (!f.typeOfIndex(inst).isNoReturn(zcu)) {
// label must be followed by an expression, include an empty one.
try writer.print("zig_block_{d}:;\n", .{block_id});
}
Expand Down Expand Up @@ -4803,6 +4837,8 @@ fn lowerTry(

try genBodyResolveState(f, inst, liveness_condbr.else_deaths, body, false);
try f.object.indent_writer.insertNewline();
if (f.object.dg.expected_block) |_|
return f.fail("runtime code not allowed in naked function", .{});
}

// Now we have the "then branch" (in terms of the liveness data); process any deaths.
Expand All @@ -4820,9 +4856,7 @@ fn lowerTry(

try reap(f, inst, &.{operand});

if (f.liveness.isUnused(inst)) {
return .none;
}
if (f.liveness.isUnused(inst)) return .none;

const local = try f.allocLocal(inst, inst_ty);
const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_ty, .complete));
Expand All @@ -4842,6 +4876,12 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !void {
const result = block.result;
const writer = f.object.writer();

if (f.object.dg.is_naked_fn) {
if (result != .none) return f.fail("runtime code not allowed in naked function", .{});
f.object.dg.expected_block = block.block_id;
return;
}

// If result is .none then the value of the block is unused.
if (result != .none) {
const operand_ty = f.typeOf(branch.operand);
Expand Down Expand Up @@ -5096,6 +5136,8 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !void {

try genBodyResolveState(f, inst, liveness_condbr.then_deaths, then_body, false);
try writer.writeByte('\n');
if (else_body.len > 0) if (f.object.dg.expected_block) |_|
return f.fail("runtime code not allowed in naked function", .{});

// We don't need to use `genBodyResolveState` for the else block, because this instruction is
// noreturn so must terminate a body, therefore we don't need to leave `value_map` or
Expand Down Expand Up @@ -5193,6 +5235,8 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void
try genBodyResolveState(f, inst, liveness.deaths[case.idx], case.body, true);
f.object.indent_writer.popIndent();
try writer.writeByte('}');
if (f.object.dg.expected_block) |_|
return f.fail("runtime code not allowed in naked function", .{});

// The case body must be noreturn so we don't need to insert a break.
}
Expand Down Expand Up @@ -5236,6 +5280,8 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void
try genBodyResolveState(f, inst, liveness.deaths[case.idx], case.body, true);
f.object.indent_writer.popIndent();
try writer.writeByte('}');
if (f.object.dg.expected_block) |_|
return f.fail("runtime code not allowed in naked function", .{});
}
}
if (is_dispatch_loop) {
Expand All @@ -5248,6 +5294,8 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void
try die(f, inst, death.toRef());
}
try genBody(f, else_body);
if (f.object.dg.expected_block) |_|
return f.fail("runtime code not allowed in naked function", .{});
} else {
try writer.writeAll("zig_unreachable();");
}
Expand Down
6 changes: 6 additions & 0 deletions src/link/C.zig
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ pub fn updateFunc(
.error_msg = null,
.pass = .{ .nav = func.owner_nav },
.is_naked_fn = Type.fromInterned(func.ty).fnCallingConvention(zcu) == .naked,
.expected_block = null,
.fwd_decl = fwd_decl.toManaged(gpa),
.ctype_pool = ctype_pool.*,
.scratch = .{},
Expand Down Expand Up @@ -272,6 +273,7 @@ fn updateUav(self: *C, pt: Zcu.PerThread, i: usize) !void {
.error_msg = null,
.pass = .{ .uav = uav },
.is_naked_fn = false,
.expected_block = null,
.fwd_decl = fwd_decl.toManaged(gpa),
.ctype_pool = codegen.CType.Pool.empty,
.scratch = .{},
Expand Down Expand Up @@ -347,6 +349,7 @@ pub fn updateNav(self: *C, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !
.error_msg = null,
.pass = .{ .nav = nav_index },
.is_naked_fn = false,
.expected_block = null,
.fwd_decl = fwd_decl.toManaged(gpa),
.ctype_pool = ctype_pool.*,
.scratch = .{},
Expand Down Expand Up @@ -675,6 +678,7 @@ fn flushErrDecls(self: *C, pt: Zcu.PerThread, ctype_pool: *codegen.CType.Pool) F
.error_msg = null,
.pass = .flush,
.is_naked_fn = false,
.expected_block = null,
.fwd_decl = fwd_decl.toManaged(gpa),
.ctype_pool = ctype_pool.*,
.scratch = .{},
Expand Down Expand Up @@ -722,6 +726,7 @@ fn flushLazyFn(
.error_msg = null,
.pass = .flush,
.is_naked_fn = false,
.expected_block = null,
.fwd_decl = fwd_decl.toManaged(gpa),
.ctype_pool = ctype_pool.*,
.scratch = .{},
Expand Down Expand Up @@ -868,6 +873,7 @@ pub fn updateExports(
.error_msg = null,
.pass = pass,
.is_naked_fn = false,
.expected_block = null,
.fwd_decl = fwd_decl.toManaged(gpa),
.ctype_pool = decl_block.ctype_pool,
.scratch = .{},
Expand Down
10 changes: 10 additions & 0 deletions test/behavior/packed-struct.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1317,3 +1317,13 @@ test "packed struct equality" {
try S.doTest(x, y);
comptime try S.doTest(x, y);
}

test "packed struct with signed field" {
var s: packed struct {
a: i2,
b: u6,
} = .{ .a = -1, .b = 42 };
s = s;
try expect(s.a == -1);
try expect(s.b == 42);
}
Loading