diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 727a76da2..e1680e846 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -396,6 +396,7 @@ typedef struct bool attr_inline : 1; bool attr_noinline : 1; bool attr_extname : 1; + bool attr_naked : 1; }; TypeInfo *type_parent; diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 122f2010f..a3bb56032 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -628,6 +628,7 @@ unsigned attribute_zext; unsigned attribute_sext; unsigned attribute_byval; unsigned attribute_inreg; +unsigned attribute_naked; void llvm_codegen_setup() { @@ -709,6 +710,7 @@ void llvm_codegen_setup() attribute_align = lookup_attribute("align"); attribute_byval = lookup_attribute("byval"); attribute_inreg = lookup_attribute("inreg"); + attribute_naked = lookup_attribute("naked"); intrinsics_setup = true; } diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index 9fa19f262..7d65aa58f 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -422,10 +422,13 @@ void llvm_emit_function_body(GenContext *context, Decl *decl) } - // Generate LLVMValueRef's for all parameters, so we can use them as local vars in code - VECEACH(decl->func_decl.function_signature.params, i) + if (!decl->func_decl.attr_naked) { - llvm_emit_parameter(context, decl->func_decl.function_signature.params[i], &arg, i); + // Generate LLVMValueRef's for all parameters, so we can use them as local vars in code + VECEACH(decl->func_decl.function_signature.params, i) + { + llvm_emit_parameter(context, decl->func_decl.function_signature.params[i], &arg, i); + } } LLVMSetCurrentDebugLocation2(context->builder, NULL); @@ -575,7 +578,10 @@ void llvm_emit_function_decl(GenContext *c, Decl *decl) LLVMSetSection(function, decl->section); } llvm_attribute_add(c, function, attribute_nounwind, -1); - + if (decl->func_decl.attr_naked) + { + llvm_attribute_add(c, function, attribute_naked, -1); + } if (decl->func_decl.function_signature.call_abi == CALL_X86_STD) { if (platform_target.os == OS_TYPE_WIN32) diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index 820ea4ec9..ed012b0c9 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -177,6 +177,7 @@ extern unsigned attribute_zext; // zero extend extern unsigned attribute_sext; // sign extend extern unsigned attribute_byval; // ByVal (param) extern unsigned attribute_inreg; // inreg (param) +extern unsigned attribute_naked; // naked function void gencontext_begin_module(GenContext *c); void gencontext_init_file_emit(GenContext *c, Context *ast); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index c641e77b3..8917164be 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -1086,6 +1086,7 @@ static inline bool sema_analyse_func(Context *context, Decl *decl) case ATTRIBUTE_INLINE: SET_ATTR(attr_inline); case ATTRIBUTE_NORETURN: SET_ATTR(attr_noreturn); case ATTRIBUTE_WEAK: SET_ATTR(attr_weak); + case ATTRIBUTE_NAKED: SET_ATTR(attr_naked); default: UNREACHABLE } diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 9e060867f..f7aeeeffa 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -2426,16 +2426,32 @@ bool sema_analyse_function_body(Context *context, Decl *func) } Ast **asserts = NULL; if (!sema_analyse_requires(context, func->docs, &asserts)) return false; - if (!sema_analyse_compound_statement_no_scope(context, func->func_decl.body)) return false; - assert(context->active_scope.depth == 1); - if (!context->active_scope.jump_end) + if (func->func_decl.attr_naked) { - Type *canonical_rtype = signature->rtype->type->canonical; - if (canonical_rtype != type_void) + Ast **stmts = func->func_decl.body->compound_stmt.stmts; + VECEACH(stmts, i) { - // IMPROVE better pointer to end. - SEMA_ERROR(func, "Missing return statement at the end of the function."); - return false; + if (stmts[i]->ast_kind != AST_ASM_STMT) + { + SEMA_ERROR(stmts[i], "Only asm statements are allowed inside of a naked function."); + return false; + } + } + asserts = NULL; + } + else + { + if (!sema_analyse_compound_statement_no_scope(context, func->func_decl.body)) return false; + assert(context->active_scope.depth == 1); + if (!context->active_scope.jump_end) + { + Type *canonical_rtype = signature->rtype->type->canonical; + if (canonical_rtype != type_void) + { + // IMPROVE better pointer to end. + SEMA_ERROR(func, "Missing return statement at the end of the function."); + return false; + } } } if (asserts) diff --git a/test/test_suite/functions/naked_function.c3t b/test/test_suite/functions/naked_function.c3t new file mode 100644 index 000000000..03fd2a13a --- /dev/null +++ b/test/test_suite/functions/naked_function.c3t @@ -0,0 +1,15 @@ +// #target: x64-darwin + +func void test(int i) @naked +{ + +} + +// #expect: naked_function.ll + +define void @naked_function.test(i32 %0) #0 { +entry: + ret void +} + +attributes #0 = { naked nounwind } \ No newline at end of file diff --git a/test/test_suite/functions/naked_function_fail.c3 b/test/test_suite/functions/naked_function_fail.c3 new file mode 100644 index 000000000..ac0c34aac --- /dev/null +++ b/test/test_suite/functions/naked_function_fail.c3 @@ -0,0 +1,4 @@ +func void test() @naked +{ + int i; // #error: Only asm statements are allowed inside of a naked function +} \ No newline at end of file