| ... | @@ -105,7 +105,7 @@ pub const CValue = union(enum) { | ... | @@ -105,7 +105,7 @@ pub const CValue = union(enum) { |
| 105 | }; | 105 | }; |
| 106 | | 106 | |
| 107 | const BlockData = struct { | 107 | const BlockData = struct { |
| 108 | block_id: usize, | 108 | block_id: u32, |
| 109 | result: CValue, | 109 | result: CValue, |
| 110 | }; | 110 | }; |
| 111 | | 111 | |
| ... | @@ -359,8 +359,8 @@ pub const Function = struct { | ... | @@ -359,8 +359,8 @@ pub const Function = struct { |
| 359 | liveness: Liveness, | 359 | liveness: Liveness, |
| 360 | value_map: CValueMap, | 360 | value_map: CValueMap, |
| 361 | blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .empty, | 361 | blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .empty, |
| 362 | next_arg_index: usize = 0, | 362 | next_arg_index: u32 = 0, |
| 363 | next_block_index: usize = 0, | 363 | next_block_index: u32 = 0, |
| 364 | object: Object, | 364 | object: Object, |
| 365 | lazy_fns: LazyFnMap, | 365 | lazy_fns: LazyFnMap, |
| 366 | func_index: InternPool.Index, | 366 | func_index: InternPool.Index, |
| ... | @@ -663,6 +663,7 @@ pub const DeclGen = struct { | ... | @@ -663,6 +663,7 @@ pub const DeclGen = struct { |
| 663 | mod: *Module, | 663 | mod: *Module, |
| 664 | pass: Pass, | 664 | pass: Pass, |
| 665 | is_naked_fn: bool, | 665 | is_naked_fn: bool, |
| | 666 | expected_block: ?u32, |
| 666 | /// This is a borrowed reference from `link.C`. | 667 | /// This is a borrowed reference from `link.C`. |
| 667 | fwd_decl: std.ArrayList(u8), | 668 | fwd_decl: std.ArrayList(u8), |
| 668 | error_msg: ?*Zcu.ErrorMsg, | 669 | error_msg: ?*Zcu.ErrorMsg, |
| ... | @@ -1399,12 +1400,24 @@ pub const DeclGen = struct { | ... | @@ -1399,12 +1400,24 @@ pub const DeclGen = struct { |
| 1399 | .repeated_elem => |elem| elem, | 1400 | .repeated_elem => |elem| elem, |
| 1400 | }; | 1401 | }; |
| 1401 | | 1402 | |
| | 1403 | const field_int_info: std.builtin.Type.Int = if (field_ty.isAbiInt(zcu)) |
| | 1404 | field_ty.intInfo(zcu) |
| | 1405 | else |
| | 1406 | .{ .signedness = .unsigned, .bits = undefined }; |
| | 1407 | switch (field_int_info.signedness) { |
| | 1408 | .signed => { |
| | 1409 | try writer.writeByte('('); |
| | 1410 | try dg.renderValue(writer, Value.fromInterned(field_val), .Other); |
| | 1411 | try writer.writeAll(" & "); |
| | 1412 | const field_uint_ty = try pt.intType(.unsigned, field_int_info.bits); |
| | 1413 | try dg.renderValue(writer, try field_uint_ty.maxIntScalar(pt, field_uint_ty), .Other); |
| | 1414 | try writer.writeByte(')'); |
| | 1415 | }, |
| | 1416 | .unsigned => try dg.renderValue(writer, Value.fromInterned(field_val), .Other), |
| | 1417 | } |
| 1402 | if (bit_offset != 0) { | 1418 | if (bit_offset != 0) { |
| 1403 | try dg.renderValue(writer, Value.fromInterned(field_val), .Other); | | |
| 1404 | try writer.writeAll(" << "); | 1419 | try writer.writeAll(" << "); |
| 1405 | try dg.renderValue(writer, try pt.intValue(bit_offset_ty, bit_offset), .FunctionArgument); | 1420 | try dg.renderValue(writer, try pt.intValue(bit_offset_ty, bit_offset), .FunctionArgument); |
| 1406 | } else { | | |
| 1407 | try dg.renderValue(writer, Value.fromInterned(field_val), .Other); | | |
| 1408 | } | 1421 | } |
| 1409 | | 1422 | |
| 1410 | bit_offset += field_ty.bitSize(zcu); | 1423 | bit_offset += field_ty.bitSize(zcu); |
| ... | @@ -2899,6 +2912,8 @@ pub fn genFunc(f: *Function) !void { | ... | @@ -2899,6 +2912,8 @@ pub fn genFunc(f: *Function) !void { |
| 2899 | const main_body = f.air.getMainBody(); | 2912 | const main_body = f.air.getMainBody(); |
| 2900 | try genBodyResolveState(f, undefined, &.{}, main_body, false); | 2913 | try genBodyResolveState(f, undefined, &.{}, main_body, false); |
| 2901 | try o.indent_writer.insertNewline(); | 2914 | try o.indent_writer.insertNewline(); |
| | 2915 | if (o.dg.expected_block) |_| |
| | 2916 | return f.fail("runtime code not allowed in naked function", .{}); |
| 2902 | | 2917 | |
| 2903 | // Take advantage of the free_locals map to bucket locals per type. All | 2918 | // Take advantage of the free_locals map to bucket locals per type. All |
| 2904 | // locals corresponding to AIR instructions should be in there due to | 2919 | // locals corresponding to AIR instructions should be in there due to |
| ... | @@ -3189,6 +3204,8 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, | ... | @@ -3189,6 +3204,8 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, |
| 3189 | const air_datas = f.air.instructions.items(.data); | 3204 | const air_datas = f.air.instructions.items(.data); |
| 3190 | | 3205 | |
| 3191 | for (body) |inst| { | 3206 | for (body) |inst| { |
| | 3207 | if (f.object.dg.expected_block) |_| |
| | 3208 | return f.fail("runtime code not allowed in naked function", .{}); |
| 3192 | if (f.liveness.isUnused(inst) and !f.air.mustLower(inst, ip)) | 3209 | if (f.liveness.isUnused(inst) and !f.air.mustLower(inst, ip)) |
| 3193 | continue; | 3210 | continue; |
| 3194 | | 3211 | |
| ... | @@ -4517,6 +4534,7 @@ fn airCall( | ... | @@ -4517,6 +4534,7 @@ fn airCall( |
| 4517 | ) !CValue { | 4534 | ) !CValue { |
| 4518 | const pt = f.object.dg.pt; | 4535 | const pt = f.object.dg.pt; |
| 4519 | const zcu = pt.zcu; | 4536 | const zcu = pt.zcu; |
| | 4537 | const ip = &zcu.intern_pool; |
| 4520 | // Not even allowed to call panic in a naked function. | 4538 | // Not even allowed to call panic in a naked function. |
| 4521 | if (f.object.dg.is_naked_fn) return .none; | 4539 | if (f.object.dg.is_naked_fn) return .none; |
| 4522 | | 4540 | |
| ... | @@ -4562,11 +4580,12 @@ fn airCall( | ... | @@ -4562,11 +4580,12 @@ fn airCall( |
| 4562 | } | 4580 | } |
| 4563 | | 4581 | |
| 4564 | const callee_ty = f.typeOf(pl_op.operand); | 4582 | const callee_ty = f.typeOf(pl_op.operand); |
| 4565 | const fn_info = zcu.typeToFunc(switch (callee_ty.zigTypeTag(zcu)) { | 4583 | const callee_is_ptr = switch (callee_ty.zigTypeTag(zcu)) { |
| 4566 | .@"fn" => callee_ty, | 4584 | .@"fn" => false, |
| 4567 | .pointer => callee_ty.childType(zcu), | 4585 | .pointer => true, |
| 4568 | else => unreachable, | 4586 | else => unreachable, |
| 4569 | }).?; | 4587 | }; |
| | 4588 | const fn_info = zcu.typeToFunc(if (callee_is_ptr) callee_ty.childType(zcu) else callee_ty).?; |
| 4570 | const ret_ty = Type.fromInterned(fn_info.return_type); | 4589 | const ret_ty = Type.fromInterned(fn_info.return_type); |
| 4571 | const ret_ctype: CType = if (ret_ty.isNoReturn(zcu)) | 4590 | const ret_ctype: CType = if (ret_ty.isNoReturn(zcu)) |
| 4572 | CType.void | 4591 | CType.void |
| ... | @@ -4598,20 +4617,29 @@ fn airCall( | ... | @@ -4598,20 +4617,29 @@ fn airCall( |
| 4598 | callee: { | 4617 | callee: { |
| 4599 | known: { | 4618 | known: { |
| 4600 | const callee_val = (try f.air.value(pl_op.operand, pt)) orelse break :known; | 4619 | const callee_val = (try f.air.value(pl_op.operand, pt)) orelse break :known; |
| 4601 | const fn_nav = switch (zcu.intern_pool.indexToKey(callee_val.toIntern())) { | 4620 | const fn_nav, const need_cast = switch (ip.indexToKey(callee_val.toIntern())) { |
| 4602 | .@"extern" => |@"extern"| @"extern".owner_nav, | 4621 | .@"extern" => |@"extern"| .{ @"extern".owner_nav, false }, |
| 4603 | .func => |func| func.owner_nav, | 4622 | .func => |func| .{ func.owner_nav, Type.fromInterned(func.ty).fnCallingConvention(zcu) != .naked and |
| | 4623 | Type.fromInterned(func.uncoerced_ty).fnCallingConvention(zcu) == .naked }, |
| 4604 | .ptr => |ptr| if (ptr.byte_offset == 0) switch (ptr.base_addr) { | 4624 | .ptr => |ptr| if (ptr.byte_offset == 0) switch (ptr.base_addr) { |
| 4605 | .nav => |nav| nav, | 4625 | .nav => |nav| .{ nav, Type.fromInterned(ptr.ty).childType(zcu).fnCallingConvention(zcu) != .naked and |
| | 4626 | zcu.navValue(nav).typeOf(zcu).fnCallingConvention(zcu) == .naked }, |
| 4606 | else => break :known, | 4627 | else => break :known, |
| 4607 | } else break :known, | 4628 | } else break :known, |
| 4608 | else => break :known, | 4629 | else => break :known, |
| 4609 | }; | 4630 | }; |
| | 4631 | if (need_cast) { |
| | 4632 | try writer.writeAll("(("); |
| | 4633 | try f.renderType(writer, if (callee_is_ptr) callee_ty else try pt.singleConstPtrType(callee_ty)); |
| | 4634 | try writer.writeByte(')'); |
| | 4635 | if (!callee_is_ptr) try writer.writeByte('&'); |
| | 4636 | } |
| 4610 | switch (modifier) { | 4637 | switch (modifier) { |
| 4611 | .auto, .always_tail => try f.object.dg.renderNavName(writer, fn_nav), | 4638 | .auto, .always_tail => try f.object.dg.renderNavName(writer, fn_nav), |
| 4612 | inline .never_tail, .never_inline => |m| try writer.writeAll(try f.getLazyFnName(@unionInit(LazyFnKey, @tagName(m), fn_nav))), | 4639 | inline .never_tail, .never_inline => |m| try writer.writeAll(try f.getLazyFnName(@unionInit(LazyFnKey, @tagName(m), fn_nav))), |
| 4613 | else => unreachable, | 4640 | else => unreachable, |
| 4614 | } | 4641 | } |
| | 4642 | if (need_cast) try writer.writeByte(')'); |
| 4615 | break :callee; | 4643 | break :callee; |
| 4616 | } | 4644 | } |
| 4617 | switch (modifier) { | 4645 | switch (modifier) { |
| ... | @@ -4712,7 +4740,7 @@ fn lowerBlock(f: *Function, inst: Air.Inst.Index, body: []const Air.Inst.Index) | ... | @@ -4712,7 +4740,7 @@ fn lowerBlock(f: *Function, inst: Air.Inst.Index, body: []const Air.Inst.Index) |
| 4712 | const zcu = pt.zcu; | 4740 | const zcu = pt.zcu; |
| 4713 | const liveness_block = f.liveness.getBlock(inst); | 4741 | const liveness_block = f.liveness.getBlock(inst); |
| 4714 | | 4742 | |
| 4715 | const block_id: usize = f.next_block_index; | 4743 | const block_id = f.next_block_index; |
| 4716 | f.next_block_index += 1; | 4744 | f.next_block_index += 1; |
| 4717 | const writer = f.object.writer(); | 4745 | const writer = f.object.writer(); |
| 4718 | | 4746 | |
| ... | @@ -4739,7 +4767,13 @@ fn lowerBlock(f: *Function, inst: Air.Inst.Index, body: []const Air.Inst.Index) | ... | @@ -4739,7 +4767,13 @@ fn lowerBlock(f: *Function, inst: Air.Inst.Index, body: []const Air.Inst.Index) |
| 4739 | try f.object.indent_writer.insertNewline(); | 4767 | try f.object.indent_writer.insertNewline(); |
| 4740 | | 4768 | |
| 4741 | // noreturn blocks have no `br` instructions reaching them, so we don't want a label | 4769 | // noreturn blocks have no `br` instructions reaching them, so we don't want a label |
| 4742 | if (!f.typeOfIndex(inst).isNoReturn(zcu)) { | 4770 | if (f.object.dg.is_naked_fn) { |
| | 4771 | if (f.object.dg.expected_block) |expected_block| { |
| | 4772 | if (block_id != expected_block) |
| | 4773 | return f.fail("runtime code not allowed in naked function", .{}); |
| | 4774 | f.object.dg.expected_block = null; |
| | 4775 | } |
| | 4776 | } else if (!f.typeOfIndex(inst).isNoReturn(zcu)) { |
| 4743 | // label must be followed by an expression, include an empty one. | 4777 | // label must be followed by an expression, include an empty one. |
| 4744 | try writer.print("zig_block_{d}:;\n", .{block_id}); | 4778 | try writer.print("zig_block_{d}:;\n", .{block_id}); |
| 4745 | } | 4779 | } |
| ... | @@ -4803,6 +4837,8 @@ fn lowerTry( | ... | @@ -4803,6 +4837,8 @@ fn lowerTry( |
| 4803 | | 4837 | |
| 4804 | try genBodyResolveState(f, inst, liveness_condbr.else_deaths, body, false); | 4838 | try genBodyResolveState(f, inst, liveness_condbr.else_deaths, body, false); |
| 4805 | try f.object.indent_writer.insertNewline(); | 4839 | try f.object.indent_writer.insertNewline(); |
| | 4840 | if (f.object.dg.expected_block) |_| |
| | 4841 | return f.fail("runtime code not allowed in naked function", .{}); |
| 4806 | } | 4842 | } |
| 4807 | | 4843 | |
| 4808 | // Now we have the "then branch" (in terms of the liveness data); process any deaths. | 4844 | // Now we have the "then branch" (in terms of the liveness data); process any deaths. |
| ... | @@ -4820,9 +4856,7 @@ fn lowerTry( | ... | @@ -4820,9 +4856,7 @@ fn lowerTry( |
| 4820 | | 4856 | |
| 4821 | try reap(f, inst, &.{operand}); | 4857 | try reap(f, inst, &.{operand}); |
| 4822 | | 4858 | |
| 4823 | if (f.liveness.isUnused(inst)) { | 4859 | if (f.liveness.isUnused(inst)) return .none; |
| 4824 | return .none; | | |
| 4825 | } | | |
| 4826 | | 4860 | |
| 4827 | const local = try f.allocLocal(inst, inst_ty); | 4861 | const local = try f.allocLocal(inst, inst_ty); |
| 4828 | const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_ty, .complete)); | 4862 | const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_ty, .complete)); |
| ... | @@ -4842,6 +4876,12 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !void { | ... | @@ -4842,6 +4876,12 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !void { |
| 4842 | const result = block.result; | 4876 | const result = block.result; |
| 4843 | const writer = f.object.writer(); | 4877 | const writer = f.object.writer(); |
| 4844 | | 4878 | |
| | 4879 | if (f.object.dg.is_naked_fn) { |
| | 4880 | if (result != .none) return f.fail("runtime code not allowed in naked function", .{}); |
| | 4881 | f.object.dg.expected_block = block.block_id; |
| | 4882 | return; |
| | 4883 | } |
| | 4884 | |
| 4845 | // If result is .none then the value of the block is unused. | 4885 | // If result is .none then the value of the block is unused. |
| 4846 | if (result != .none) { | 4886 | if (result != .none) { |
| 4847 | const operand_ty = f.typeOf(branch.operand); | 4887 | const operand_ty = f.typeOf(branch.operand); |
| ... | @@ -5096,6 +5136,8 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !void { | ... | @@ -5096,6 +5136,8 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !void { |
| 5096 | | 5136 | |
| 5097 | try genBodyResolveState(f, inst, liveness_condbr.then_deaths, then_body, false); | 5137 | try genBodyResolveState(f, inst, liveness_condbr.then_deaths, then_body, false); |
| 5098 | try writer.writeByte('\n'); | 5138 | try writer.writeByte('\n'); |
| | 5139 | if (else_body.len > 0) if (f.object.dg.expected_block) |_| |
| | 5140 | return f.fail("runtime code not allowed in naked function", .{}); |
| 5099 | | 5141 | |
| 5100 | // We don't need to use `genBodyResolveState` for the else block, because this instruction is | 5142 | // We don't need to use `genBodyResolveState` for the else block, because this instruction is |
| 5101 | // noreturn so must terminate a body, therefore we don't need to leave `value_map` or | 5143 | // noreturn so must terminate a body, therefore we don't need to leave `value_map` or |
| ... | @@ -5193,6 +5235,8 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void | ... | @@ -5193,6 +5235,8 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void |
| 5193 | try genBodyResolveState(f, inst, liveness.deaths[case.idx], case.body, true); | 5235 | try genBodyResolveState(f, inst, liveness.deaths[case.idx], case.body, true); |
| 5194 | f.object.indent_writer.popIndent(); | 5236 | f.object.indent_writer.popIndent(); |
| 5195 | try writer.writeByte('}'); | 5237 | try writer.writeByte('}'); |
| | 5238 | if (f.object.dg.expected_block) |_| |
| | 5239 | return f.fail("runtime code not allowed in naked function", .{}); |
| 5196 | | 5240 | |
| 5197 | // The case body must be noreturn so we don't need to insert a break. | 5241 | // The case body must be noreturn so we don't need to insert a break. |
| 5198 | } | 5242 | } |
| ... | @@ -5236,6 +5280,8 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void | ... | @@ -5236,6 +5280,8 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void |
| 5236 | try genBodyResolveState(f, inst, liveness.deaths[case.idx], case.body, true); | 5280 | try genBodyResolveState(f, inst, liveness.deaths[case.idx], case.body, true); |
| 5237 | f.object.indent_writer.popIndent(); | 5281 | f.object.indent_writer.popIndent(); |
| 5238 | try writer.writeByte('}'); | 5282 | try writer.writeByte('}'); |
| | 5283 | if (f.object.dg.expected_block) |_| |
| | 5284 | return f.fail("runtime code not allowed in naked function", .{}); |
| 5239 | } | 5285 | } |
| 5240 | } | 5286 | } |
| 5241 | if (is_dispatch_loop) { | 5287 | if (is_dispatch_loop) { |
| ... | @@ -5248,6 +5294,8 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void | ... | @@ -5248,6 +5294,8 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void |
| 5248 | try die(f, inst, death.toRef()); | 5294 | try die(f, inst, death.toRef()); |
| 5249 | } | 5295 | } |
| 5250 | try genBody(f, else_body); | 5296 | try genBody(f, else_body); |
| | 5297 | if (f.object.dg.expected_block) |_| |
| | 5298 | return f.fail("runtime code not allowed in naked function", .{}); |
| 5251 | } else { | 5299 | } else { |
| 5252 | try writer.writeAll("zig_unreachable();"); | 5300 | try writer.writeAll("zig_unreachable();"); |
| 5253 | } | 5301 | } |