Commit c098a8f522

Andrew Kelley <superjoe30@gmail.com>
2016-05-07 08:10:14
add frame_address and return_address builtins
1 parent 271a37b
src/all_types.hpp
@@ -1055,6 +1055,7 @@ struct FnTableEntry {
     bool is_test;
     bool is_pure;
     bool safety_off;
+    bool is_noinline;
     BlockContext *parent_block_context;
     FnAnalState anal_state;
 
@@ -1116,6 +1117,8 @@ enum BuiltinFnId {
     BuiltinFnIdCImport,
     BuiltinFnIdErrName,
     BuiltinFnIdBreakpoint,
+    BuiltinFnIdReturnAddress,
+    BuiltinFnIdFrameAddress,
     BuiltinFnIdEmbedFile,
     BuiltinFnIdCmpExchange,
     BuiltinFnIdFence,
src/analyze.cpp
@@ -980,6 +980,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
     bool is_cold = false;
     bool is_naked = false;
     bool is_test = false;
+    bool is_noinline = false;
 
     if (fn_proto->top_level_decl.directives) {
         for (int i = 0; i < fn_proto->top_level_decl.directives->length; i += 1) {
@@ -993,6 +994,8 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
                     if (attr_name) {
                         if (buf_eql_str(attr_name, "naked")) {
                             is_naked = true;
+                        } else if (buf_eql_str(attr_name, "noinline")) {
+                            is_noinline = true;
                         } else if (buf_eql_str(attr_name, "cold")) {
                             is_cold = true;
                         } else if (buf_eql_str(attr_name, "test")) {
@@ -1062,12 +1065,20 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
 
     fn_table_entry->type_entry = fn_type;
     fn_table_entry->is_test = is_test;
+    fn_table_entry->is_noinline = is_noinline;
 
     if (fn_type->id == TypeTableEntryIdInvalid) {
         fn_proto->skip = true;
         return;
     }
 
+    if (fn_table_entry->is_inline && fn_table_entry->is_noinline) {
+        add_node_error(g, node, buf_sprintf("function is both inline and noinline"));
+        fn_proto->skip = true;
+        return;
+    }
+
+
     Buf *symbol_name;
     if (is_c_compat) {
         symbol_name = &fn_table_entry->symbol_name;
@@ -1081,6 +1092,9 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
     if (fn_table_entry->is_inline) {
         LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMAlwaysInlineAttribute);
     }
+    if (fn_table_entry->is_noinline) {
+        LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMNoInlineAttribute);
+    }
     if (fn_type->data.fn.fn_type_id.is_naked) {
         LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMNakedAttribute);
     }
@@ -4866,6 +4880,10 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry
         case BuiltinFnIdBreakpoint:
             mark_impure_fn(context);
             return g->builtin_types.entry_void;
+        case BuiltinFnIdReturnAddress:
+        case BuiltinFnIdFrameAddress:
+            mark_impure_fn(context);
+            return builtin_fn->return_type;
         case BuiltinFnIdEmbedFile:
             return analyze_embed_file(g, import, context, node);
         case BuiltinFnIdCmpExchange:
src/codegen.cpp
@@ -634,6 +634,13 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
         case BuiltinFnIdBreakpoint:
             set_debug_source_node(g, node);
             return LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, "");
+        case BuiltinFnIdFrameAddress:
+        case BuiltinFnIdReturnAddress:
+            {
+                LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_i32->type_ref);
+                set_debug_source_node(g, node);
+                return LLVMBuildCall(g->builder, builtin_fn->fn_val, &zero, 1, "");
+            }
         case BuiltinFnIdCmpExchange:
             return gen_cmp_exchange(g, node);
         case BuiltinFnIdFence:
@@ -4330,6 +4337,26 @@ static void define_builtin_fns(CodeGen *g) {
 
         g->trap_fn_val = builtin_fn->fn_val;
     }
+    {
+        BuiltinFnEntry *builtin_fn = create_builtin_fn_with_arg_count(g, BuiltinFnIdReturnAddress,
+                "return_address", 0);
+        builtin_fn->return_type = get_pointer_to_type(g, g->builtin_types.entry_u8, true);
+
+        LLVMTypeRef fn_type = LLVMFunctionType(builtin_fn->return_type->type_ref,
+                &g->builtin_types.entry_i32->type_ref, 1, false);
+        builtin_fn->fn_val = LLVMAddFunction(g->module, "llvm.returnaddress", fn_type);
+        assert(LLVMGetIntrinsicID(builtin_fn->fn_val));
+    }
+    {
+        BuiltinFnEntry *builtin_fn = create_builtin_fn_with_arg_count(g, BuiltinFnIdFrameAddress,
+                "frame_address", 0);
+        builtin_fn->return_type = get_pointer_to_type(g, g->builtin_types.entry_u8, true);
+
+        LLVMTypeRef fn_type = LLVMFunctionType(builtin_fn->return_type->type_ref,
+                &g->builtin_types.entry_i32->type_ref, 1, false);
+        builtin_fn->fn_val = LLVMAddFunction(g->module, "llvm.frameaddress", fn_type);
+        assert(LLVMGetIntrinsicID(builtin_fn->fn_val));
+    }
     {
         BuiltinFnEntry *builtin_fn = create_builtin_fn(g, BuiltinFnIdMemcpy, "memcpy");
         builtin_fn->return_type = g->builtin_types.entry_void;
src/eval.cpp
@@ -787,6 +787,8 @@ static bool eval_fn_call_builtin(EvalFn *ef, AstNode *node, ConstExprValue *out_
             zig_panic("TODO");
         case BuiltinFnIdBreakpoint:
         case BuiltinFnIdInvalid:
+        case BuiltinFnIdFrameAddress:
+        case BuiltinFnIdReturnAddress:
             zig_unreachable();
     }