Commit 2ba787e303

Jacob Young <jacobly0@users.noreply.github.com>
2023-07-31 07:50:54
Sema: restrict what can appear in a naked function
* Disable runtime calls, since it is not possible to know the proper stack adjustment to follow the callee abi. * Disable runtime returns, since it is not possible to know where the return address is stored in general. * Allow implicit returns regardless of the return type, which allows naked functions with a non-void return type to be written.
1 parent 9831f27
Changed files (2)
src/Module.zig
@@ -4186,6 +4186,7 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void {
             .owner_decl = new_decl,
             .owner_decl_index = new_decl_index,
             .func_index = .none,
+            .func_is_naked = false,
             .fn_ret_ty = Type.void,
             .fn_ret_ty_ies = null,
             .owner_func_index = .none,
@@ -4268,6 +4269,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
         .owner_decl = decl,
         .owner_decl_index = decl_index,
         .func_index = .none,
+        .func_is_naked = false,
         .fn_ret_ty = Type.void,
         .fn_ret_ty_ies = null,
         .owner_func_index = .none,
@@ -5213,6 +5215,7 @@ pub fn analyzeFnBody(mod: *Module, func_index: InternPool.Index, arena: Allocato
         .owner_decl = decl,
         .owner_decl_index = decl_index,
         .func_index = func_index,
+        .func_is_naked = fn_ty_info.cc == .Naked,
         .fn_ret_ty = fn_ty_info.return_type.toType(),
         .fn_ret_ty_ies = null,
         .owner_func_index = func_index,
src/Sema.zig
@@ -30,6 +30,8 @@ owner_func_index: InternPool.Index,
 /// an inline or comptime function call.
 /// This could be `none`, a `func_decl`, or a `func_instance`.
 func_index: InternPool.Index,
+/// Whether the type of func_index has a calling convention of `.Naked`.
+func_is_naked: bool,
 /// Used to restore the error return trace when returning a non-error from a function.
 error_return_trace_index_on_fn_entry: Air.Inst.Ref = .none,
 /// When semantic analysis needs to know the return type of the function whose body
@@ -6827,6 +6829,10 @@ fn analyzeCall(
     var is_inline_call = is_comptime_call or modifier == .always_inline or
         func_ty_info.cc == .Inline;
 
+    if (sema.func_is_naked and !is_inline_call and !is_comptime_call) {
+        return sema.fail(block, call_src, "runtime call not allowed in naked function", .{});
+    }
+
     if (!is_inline_call and is_generic_call) {
         if (sema.instantiateGenericCall(
             block,
@@ -7509,6 +7515,9 @@ fn instantiateGenericCall(
         .owner_decl = sema.owner_decl,
         .owner_decl_index = sema.owner_decl_index,
         .func_index = sema.owner_func_index,
+        // This may not be known yet, since the calling convention could be generic, but there
+        // should be no illegal instructions encountered while creating the function anyway.
+        .func_is_naked = false,
         .fn_ret_ty = Type.void,
         .fn_ret_ty_ies = null,
         .owner_func_index = .none,
@@ -18193,10 +18202,20 @@ fn zirRetImplicit(
     const tracy = trace(@src());
     defer tracy.end();
 
+    if (block.inlining == null and sema.func_is_naked) {
+        assert(!block.is_comptime);
+        if (block.wantSafety()) {
+            // Calling a safety function from a naked function would not be legal.
+            _ = try block.addNoOp(.trap);
+        } else {
+            try block.addUnreachable(false);
+        }
+        return always_noreturn;
+    }
+
     const mod = sema.mod;
     const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
     const operand = try sema.resolveInst(inst_data.operand);
-
     const r_brace_src = inst_data.src();
     const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 };
     const base_tag = sema.fn_ret_ty.baseZigTypeTag(mod);
@@ -18222,7 +18241,7 @@ fn zirRetImplicit(
         return sema.failWithOwnedErrorMsg(msg);
     }
 
-    return sema.analyzeRet(block, operand, .unneeded);
+    return sema.analyzeRet(block, operand, r_brace_src);
 }
 
 fn zirRetNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
@@ -18244,7 +18263,7 @@ fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir
     const src = inst_data.src();
     const ret_ptr = try sema.resolveInst(inst_data.operand);
 
-    if (block.is_comptime or block.inlining != null) {
+    if (block.is_comptime or block.inlining != null or sema.func_is_naked) {
         const operand = try sema.analyzeLoad(block, src, ret_ptr, src);
         return sema.analyzeRet(block, operand, src);
     }
@@ -18450,6 +18469,8 @@ fn analyzeRet(
         return always_noreturn;
     } else if (block.is_comptime) {
         return sema.fail(block, src, "function called at runtime cannot return value at comptime", .{});
+    } else if (sema.func_is_naked) {
+        return sema.fail(block, src, "cannot return from naked function", .{});
     }
 
     try sema.resolveTypeLayout(sema.fn_ret_ty);
@@ -33571,6 +33592,7 @@ fn semaBackingIntType(mod: *Module, struct_obj: *Module.Struct) CompileError!voi
             .owner_decl = decl,
             .owner_decl_index = decl_index,
             .func_index = .none,
+            .func_is_naked = false,
             .fn_ret_ty = Type.void,
             .fn_ret_ty_ies = null,
             .owner_func_index = .none,
@@ -33622,6 +33644,7 @@ fn semaBackingIntType(mod: *Module, struct_obj: *Module.Struct) CompileError!voi
                 .owner_decl = decl,
                 .owner_decl_index = decl_index,
                 .func_index = .none,
+                .func_is_naked = false,
                 .fn_ret_ty = Type.void,
                 .fn_ret_ty_ies = null,
                 .owner_func_index = .none,
@@ -34405,6 +34428,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void
         .owner_decl = decl,
         .owner_decl_index = decl_index,
         .func_index = .none,
+        .func_is_naked = false,
         .fn_ret_ty = Type.void,
         .fn_ret_ty_ies = null,
         .owner_func_index = .none,
@@ -34748,6 +34772,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
         .owner_decl = decl,
         .owner_decl_index = decl_index,
         .func_index = .none,
+        .func_is_naked = false,
         .fn_ret_ty = Type.void,
         .fn_ret_ty_ies = null,
         .owner_func_index = .none,